From fa6cbe5abe8fa06f9a2d117bec121cf7a6ac4d09 Mon Sep 17 00:00:00 2001 From: firewave Date: Fri, 11 Aug 2023 00:54:12 +0200 Subject: [PATCH 1/2] extracted code for actual check implementation from `Check` into `CheckImpl` --- Makefile | 62 ++-- lib/check.cpp | 59 --- lib/check.h | 48 +-- lib/check64bit.cpp | 43 ++- lib/check64bit.h | 35 +- lib/checkassert.cpp | 38 +- lib/checkassert.h | 32 +- lib/checkautovariables.cpp | 111 ++++-- lib/checkautovariables.h | 75 +--- lib/checkbool.cpp | 139 +++++-- lib/checkbool.h | 88 +---- lib/checkboost.cpp | 38 +- lib/checkboost.h | 31 +- lib/checkbufferoverrun.cpp | 123 ++++-- lib/checkbufferoverrun.h | 71 +--- lib/checkclass.cpp | 616 ++++++++++++++++++++++++++----- lib/checkclass.h | 334 ++--------------- lib/checkcondition.cpp | 228 ++++++++++-- lib/checkcondition.h | 161 +------- lib/checkexceptionsafety.cpp | 105 +++++- lib/checkexceptionsafety.h | 72 +--- lib/checkfunctions.cpp | 156 ++++++-- lib/checkfunctions.h | 113 +----- lib/checkimpl.cpp | 83 +++++ lib/checkimpl.h | 81 ++++ lib/checkinternal.cpp | 100 ++++- lib/checkinternal.h | 75 +--- lib/checkio.cpp | 207 ++++++++--- lib/checkio.h | 129 +------ lib/checkleakautovar.cpp | 168 ++++++++- lib/checkleakautovar.h | 132 +------ lib/checkmemoryleak.cpp | 364 ++++++++++++++---- lib/checkmemoryleak.h | 254 +------------ lib/checknullpointer.cpp | 105 ++++-- lib/checknullpointer.h | 70 +--- lib/checkother.cpp | 612 +++++++++++++++++++++++------- lib/checkother.h | 316 +--------------- lib/checkpostfixoperator.cpp | 35 +- lib/checkpostfixoperator.h | 33 +- lib/checksizeof.cpp | 119 +++++- lib/checksizeof.h | 82 +--- lib/checkstl.cpp | 371 +++++++++++++++---- lib/checkstl.h | 242 +----------- lib/checkstring.cpp | 104 +++++- lib/checkstring.h | 73 +--- lib/checktype.cpp | 95 ++++- lib/checktype.h | 65 +--- lib/checkuninitvar.cpp | 182 ++++++--- lib/checkuninitvar.h | 77 +--- lib/checkunusedfunctions.cpp | 61 ++- lib/checkunusedfunctions.h | 14 +- lib/checkunusedvar.cpp | 215 +++++++---- lib/checkunusedvar.h | 62 +--- lib/checkvaarg.cpp | 50 ++- lib/checkvaarg.h | 38 +- lib/cppcheck.cpp | 14 +- lib/cppcheck.vcxproj | 2 + lib/lib.pri | 2 + test/test64bit.cpp | 3 +- test/testcharvar.cpp | 3 +- test/testclass.cpp | 54 +-- test/testconstructors.cpp | 6 +- test/testincompletestatement.cpp | 3 +- test/testio.cpp | 3 +- test/testother.cpp | 6 +- test/testpostfixoperator.cpp | 3 +- test/testunusedprivfunc.cpp | 3 +- 67 files changed, 3905 insertions(+), 3589 deletions(-) create mode 100644 lib/checkimpl.cpp create mode 100644 lib/checkimpl.h diff --git a/Makefile b/Makefile index 09c40c0e234..6220ebafedb 100644 --- a/Makefile +++ b/Makefile @@ -208,6 +208,7 @@ LIBOBJ = $(libcppdir)/valueflow.o \ $(libcppdir)/checkersreport.o \ $(libcppdir)/checkexceptionsafety.o \ $(libcppdir)/checkfunctions.o \ + $(libcppdir)/checkimpl.o \ $(libcppdir)/checkinternal.o \ $(libcppdir)/checkio.o \ $(libcppdir)/checkleakautovar.o \ @@ -458,7 +459,7 @@ validateRules: ###### Build -$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.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 lib/valueptr.h lib/vfvalue.h +$(libcppdir)/valueflow.o: lib/valueflow.cpp lib/addoninfo.h lib/analyzer.h lib/astutils.h lib/calculate.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/findtoken.h lib/forwardanalyzer.h lib/infer.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/programmemory.h lib/reverseanalyzer.h lib/settings.h lib/smallvector.h lib/sourcelocation.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 lib/valueptr.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/valueflow.cpp $(libcppdir)/tokenize.o: lib/tokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/summaries.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 lib/vfvalue.h @@ -473,34 +474,34 @@ $(libcppdir)/addoninfo.o: lib/addoninfo.cpp externals/picojson/picojson.h lib/ad $(libcppdir)/analyzerinfo.o: lib/analyzerinfo.cpp externals/tinyxml2/tinyxml2.h lib/analyzerinfo.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/filesettings.h lib/path.h lib/platform.h lib/standards.h lib/utils.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/analyzerinfo.cpp -$(libcppdir)/astutils.o: lib/astutils.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errortypes.h lib/findtoken.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h +$(libcppdir)/astutils.o: lib/astutils.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/config.h lib/errortypes.h lib/findtoken.h lib/infer.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/utils.h lib/valueflow.h lib/valueptr.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/astutils.cpp $(libcppdir)/check.o: lib/check.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check.cpp -$(libcppdir)/check64bit.o: lib/check64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/check64bit.o: lib/check64bit.cpp lib/addoninfo.h lib/check.h lib/check64bit.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/check64bit.cpp -$(libcppdir)/checkassert.o: lib/checkassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkassert.o: lib/checkassert.cpp lib/addoninfo.h lib/check.h lib/checkassert.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkassert.cpp -$(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkautovariables.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checkautovariables.o: lib/checkautovariables.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkautovariables.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkautovariables.cpp -$(libcppdir)/checkbool.o: lib/checkbool.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbool.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkbool.o: lib/checkbool.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbool.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbool.cpp -$(libcppdir)/checkboost.o: lib/checkboost.cpp lib/check.h lib/checkboost.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkboost.o: lib/checkboost.cpp lib/check.h lib/checkboost.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/sourcelocation.h lib/standards.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkboost.cpp -$(libcppdir)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbufferoverrun.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h lib/xml.h +$(libcppdir)/checkbufferoverrun.o: lib/checkbufferoverrun.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkbufferoverrun.h lib/checkimpl.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkbufferoverrun.cpp -$(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h +$(libcppdir)/checkclass.o: lib/checkclass.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkclass.h lib/checkimpl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkclass.cpp -$(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkcondition.h lib/checkother.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkcondition.o: lib/checkcondition.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkcondition.h lib/checkimpl.h lib/checkother.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkcondition.cpp $(libcppdir)/checkers.o: lib/checkers.cpp lib/checkers.h lib/config.h @@ -509,55 +510,58 @@ $(libcppdir)/checkers.o: lib/checkers.cpp lib/checkers.h lib/config.h $(libcppdir)/checkersreport.o: lib/checkersreport.cpp lib/addoninfo.h lib/checkers.h lib/checkersreport.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/utils.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkersreport.cpp -$(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/addoninfo.h lib/check.h lib/checkexceptionsafety.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkexceptionsafety.o: lib/checkexceptionsafety.cpp lib/addoninfo.h lib/check.h lib/checkexceptionsafety.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkexceptionsafety.cpp -$(libcppdir)/checkfunctions.o: lib/checkfunctions.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkfunctions.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checkfunctions.o: lib/checkfunctions.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkfunctions.h lib/checkimpl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkfunctions.cpp -$(libcppdir)/checkinternal.o: lib/checkinternal.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkinternal.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkimpl.o: lib/checkimpl.cpp lib/addoninfo.h lib/check.h lib/checkimpl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h + $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkimpl.cpp + +$(libcppdir)/checkinternal.o: lib/checkinternal.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkinternal.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkinternal.cpp -$(libcppdir)/checkio.o: lib/checkio.cpp lib/addoninfo.h lib/check.h lib/checkio.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkio.o: lib/checkio.cpp lib/addoninfo.h lib/check.h lib/checkimpl.h lib/checkio.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkio.cpp -$(libcppdir)/checkleakautovar.o: lib/checkleakautovar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkleakautovar.o: lib/checkleakautovar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkleakautovar.h lib/checkmemoryleak.h lib/checknullpointer.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkleakautovar.cpp -$(libcppdir)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkmemoryleak.o: lib/checkmemoryleak.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkmemoryleak.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkmemoryleak.cpp -$(libcppdir)/checknullpointer.o: lib/checknullpointer.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checknullpointer.o: lib/checknullpointer.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checknullpointer.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checknullpointer.cpp -$(libcppdir)/checkother.o: lib/checkother.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkother.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checkother.o: lib/checkother.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkother.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkother.cpp -$(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/addoninfo.h lib/check.h lib/checkpostfixoperator.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkpostfixoperator.o: lib/checkpostfixoperator.cpp lib/addoninfo.h lib/check.h lib/checkimpl.h lib/checkpostfixoperator.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkpostfixoperator.cpp -$(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/addoninfo.h lib/check.h lib/checksizeof.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checksizeof.o: lib/checksizeof.cpp lib/addoninfo.h lib/check.h lib/checkimpl.h lib/checksizeof.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checksizeof.cpp -$(libcppdir)/checkstl.o: lib/checkstl.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkstl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checkstl.o: lib/checkstl.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checknullpointer.h lib/checkstl.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/pathanalysis.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstl.cpp -$(libcppdir)/checkstring.o: lib/checkstring.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkstring.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkstring.o: lib/checkstring.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkstring.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkstring.cpp -$(libcppdir)/checktype.o: lib/checktype.cpp lib/addoninfo.h lib/check.h lib/checktype.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checktype.o: lib/checktype.cpp lib/addoninfo.h lib/check.h lib/checkimpl.h lib/checktype.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checktype.cpp -$(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checknullpointer.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkuninitvar.o: lib/checkuninitvar.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checknullpointer.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkuninitvar.cpp -$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h +$(libcppdir)/checkunusedfunctions.o: lib/checkunusedfunctions.cpp externals/tinyxml2/tinyxml2.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h lib/xml.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedfunctions.cpp -$(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h +$(libcppdir)/checkunusedvar.o: lib/checkunusedvar.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkunusedvar.h lib/config.h lib/errortypes.h lib/fwdanalysis.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkunusedvar.cpp -$(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h +$(libcppdir)/checkvaarg.o: lib/checkvaarg.cpp lib/addoninfo.h lib/astutils.h lib/check.h lib/checkimpl.h lib/checkvaarg.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/smallvector.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h $(CXX) ${INCLUDE_FOR_LIB} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $(libcppdir)/checkvaarg.cpp $(libcppdir)/clangimport.o: lib/clangimport.cpp lib/addoninfo.h lib/clangimport.h lib/config.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h @@ -830,7 +834,7 @@ test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/single test/testsizeof.o: test/testsizeof.cpp lib/addoninfo.h lib/check.h lib/checksizeof.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsizeof.cpp -test/teststl.o: test/teststl.cpp lib/addoninfo.h lib/check.h lib/checkstl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h +test/teststl.o: test/teststl.cpp lib/addoninfo.h lib/check.h lib/checkstl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/teststl.cpp test/teststring.o: test/teststring.cpp lib/addoninfo.h lib/check.h lib/checkstring.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h @@ -863,7 +867,7 @@ test/testtokenlist.o: test/testtokenlist.cpp lib/addoninfo.h lib/check.h lib/col test/testtokenrange.o: test/testtokenrange.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/vfvalue.h test/fixture.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenrange.cpp -test/testtype.o: test/testtype.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checktype.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h +test/testtype.o: test/testtype.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checktype.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtype.cpp test/testuninitvar.o: test/testuninitvar.cpp lib/addoninfo.h lib/check.h lib/checkuninitvar.h lib/color.h lib/config.h lib/ctu.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h diff --git a/lib/check.cpp b/lib/check.cpp index 53b4e5fed34..2931c75fe92 100644 --- a/lib/check.cpp +++ b/lib/check.cpp @@ -60,32 +60,6 @@ void Check::writeToErrorList(const ErrorMessage &errmsg) std::cout << errmsg.toXML() << std::endl; } - -void Check::reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) -{ - const ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); - if (mErrorLogger) - mErrorLogger->reportErr(errmsg); - else - writeToErrorList(errmsg); -} - -void Check::reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty) -{ - const ErrorMessage errmsg(errorPath, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); - if (mErrorLogger) - mErrorLogger->reportErr(errmsg); - else - writeToErrorList(errmsg); -} - -bool Check::wrongData(const Token *tok, const char *str) -{ - if (mSettings->daca) - reportError(tok, Severity::debug, "DacaWrongData", "Wrong data detected by condition " + std::string(str)); - return true; -} - std::list &Check::instances() { #ifdef __SVR4 @@ -98,36 +72,3 @@ std::list &Check::instances() return _instances; #endif } - -std::string Check::getMessageId(const ValueFlow::Value &value, const char id[]) -{ - if (value.condition != nullptr) - return id + std::string("Cond"); - if (value.safe) - return std::string("safe") + (char)std::toupper(id[0]) + (id + 1); - return id; -} - -ErrorPath Check::getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const -{ - ErrorPath errorPath; - if (!value) { - errorPath.emplace_back(errtok, std::move(bug)); - } else if (mSettings->verbose || mSettings->xml || !mSettings->templateLocation.empty()) { - errorPath = value->errorPath; - errorPath.emplace_back(errtok, std::move(bug)); - } else { - if (value->condition) - errorPath.emplace_back(value->condition, "condition '" + value->condition->expressionString() + "'"); - //else if (!value->isKnown() || value->defaultArg) - // errorPath = value->callstack; - errorPath.emplace_back(errtok, std::move(bug)); - } - return errorPath; -} - -void Check::logChecker(const char id[]) -{ - reportError(nullptr, Severity::none, "logChecker", id); -} - diff --git a/lib/check.h b/lib/check.h index 91930a37450..7eaa63c95c9 100644 --- a/lib/check.h +++ b/lib/check.h @@ -61,15 +61,8 @@ class CPPCHECKLIB Check { /** This constructor is used when registering the CheckClass */ explicit Check(const std::string &aname); -protected: - /** This constructor is used when running checks. */ - Check(std::string aname, const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger), mName(std::move(aname)) {} - -public: virtual ~Check() { - if (!mTokenizer) - instances().remove(this); + instances().remove(this); } Check(const Check &) = delete; @@ -129,45 +122,6 @@ class CPPCHECKLIB Check { return false; } -protected: - static std::string getMessageId(const ValueFlow::Value &value, const char id[]); - - const Tokenizer* const mTokenizer{}; - const Settings* const mSettings{}; - ErrorLogger* const mErrorLogger{}; - - /** report an error */ - void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg) { - reportError(tok, severity, id, msg, CWE(0U), Certainty::normal); - } - - /** report an error */ - void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) { - const std::list callstack(1, tok); - reportError(callstack, severity, id, msg, cwe, certainty); - } - - /** report an error */ - void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg) { - reportError(callstack, severity, id, msg, CWE(0U), Certainty::normal); - } - - /** report an error */ - void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty); - - void reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty); - - /** log checker */ - void logChecker(const char id[]); - - ErrorPath getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const; - - /** - * Use WRONG_DATA in checkers when you check for wrong data. That - * will call this method - */ - bool wrongData(const Token *tok, const char *str); - private: const std::string mName; }; diff --git a/lib/check64bit.cpp b/lib/check64bit.cpp index 02196cfc043..dd71a9d1e73 100644 --- a/lib/check64bit.cpp +++ b/lib/check64bit.cpp @@ -22,6 +22,7 @@ #include "check64bit.h" +#include "checkimpl.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" @@ -38,9 +39,24 @@ static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Imple // Register this check class (by creating a static instance of it) namespace { Check64BitPortability instance; + + class Check64BitPortabilityImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + Check64BitPortabilityImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** Check for pointer assignment */ + void pointerassignment(); + + void assignmentAddressToIntegerError(const Token *tok); + void assignmentIntegerToAddressError(const Token *tok); + void returnIntegerError(const Token *tok); + void returnPointerError(const Token *tok); + }; } -void Check64BitPortability::pointerassignment() +void Check64BitPortabilityImpl::pointerassignment() { if (!mSettings->severity.isEnabled(Severity::portability)) return; @@ -118,7 +134,7 @@ void Check64BitPortability::pointerassignment() } } -void Check64BitPortability::assignmentAddressToIntegerError(const Token *tok) +void Check64BitPortabilityImpl::assignmentAddressToIntegerError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentAddressToInteger", @@ -129,7 +145,7 @@ void Check64BitPortability::assignmentAddressToIntegerError(const Token *tok) "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); } -void Check64BitPortability::assignmentIntegerToAddressError(const Token *tok) +void Check64BitPortabilityImpl::assignmentIntegerToAddressError(const Token *tok) { reportError(tok, Severity::portability, "AssignmentIntegerToAddress", @@ -140,7 +156,7 @@ void Check64BitPortability::assignmentIntegerToAddressError(const Token *tok) "way is to store addresses only in pointer types (or typedefs like uintptr_t).", CWE758, Certainty::normal); } -void Check64BitPortability::returnPointerError(const Token *tok) +void Check64BitPortabilityImpl::returnPointerError(const Token *tok) { reportError(tok, Severity::portability, "CastAddressToIntegerAtReturn", @@ -151,7 +167,7 @@ void Check64BitPortability::returnPointerError(const Token *tok) "to 32-bit integer. The safe way is to always return an integer.", CWE758, Certainty::normal); } -void Check64BitPortability::returnIntegerError(const Token *tok) +void Check64BitPortabilityImpl::returnIntegerError(const Token *tok) { reportError(tok, Severity::portability, "CastIntegerToAddressAtReturn", @@ -161,3 +177,20 @@ void Check64BitPortability::returnIntegerError(const Token *tok) "and Linux they are of different width. In worst case you end up casting 64-bit integer down to 32-bit pointer. " "The safe way is to always return a pointer.", CWE758, Certainty::normal); } + + +/** @brief Run checks against the normal token list */ +void Check64BitPortability::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + Check64BitPortabilityImpl c(&tokenizer, tokenizer.getSettings(), errorLogger); + c.pointerassignment(); +} + +void Check64BitPortability::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + Check64BitPortabilityImpl c(nullptr, settings, errorLogger); + c.assignmentAddressToIntegerError(nullptr); + c.assignmentIntegerToAddressError(nullptr); + c.returnIntegerError(nullptr); + c.returnPointerError(nullptr); +} diff --git a/lib/check64bit.h b/lib/check64bit.h index 7ce3178908c..8e4beaae908 100644 --- a/lib/check64bit.h +++ b/lib/check64bit.h @@ -24,14 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; - +class Tokenizer; /// @addtogroup Checks /// @{ @@ -45,38 +43,13 @@ class CPPCHECKLIB Check64BitPortability : public Check { public: /** This constructor is used when registering the Check64BitPortability */ - Check64BitPortability() : Check(myName()) {} + Check64BitPortability() : Check("64-bit portability") {} private: - /** This constructor is used when running checks. */ - Check64BitPortability(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - Check64BitPortability check64BitPortability(&tokenizer, tokenizer.getSettings(), errorLogger); - check64BitPortability.pointerassignment(); - } - - /** Check for pointer assignment */ - void pointerassignment(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void assignmentAddressToIntegerError(const Token *tok); - void assignmentIntegerToAddressError(const Token *tok); - void returnIntegerError(const Token *tok); - void returnPointerError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - Check64BitPortability c(nullptr, settings, errorLogger); - c.assignmentAddressToIntegerError(nullptr); - c.assignmentIntegerToAddressError(nullptr); - c.returnIntegerError(nullptr); - c.returnPointerError(nullptr); - } - - static std::string myName() { - return "64-bit portability"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check if there is 64-bit portability issues:\n" diff --git a/lib/checkassert.cpp b/lib/checkassert.cpp index a96191288c2..8239810d855 100644 --- a/lib/checkassert.cpp +++ b/lib/checkassert.cpp @@ -22,6 +22,7 @@ #include "checkassert.h" +#include "checkimpl.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" @@ -37,9 +38,23 @@ static const CWE CWE398(398U); // Indicator of Poor Code Quality // Register this check class (by creating a static instance of it) namespace { CheckAssert instance; + + class CheckAssertImpl: public CheckImpl { + public: + CheckAssertImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + void assertWithSideEffects(); + + void checkVariableAssignment(const Token* assignTok, const Scope *assertionScope); + static bool inSameScope(const Token* returnTok, const Token* assignTok); + + void sideEffectInAssertError(const Token *tok, const std::string& functionName); + void assignmentInAssertError(const Token *tok, const std::string &varname); + }; } -void CheckAssert::assertWithSideEffects() +void CheckAssertImpl::assertWithSideEffects() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -99,7 +114,7 @@ void CheckAssert::assertWithSideEffects() //--------------------------------------------------------------------------- -void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& functionName) +void CheckAssertImpl::sideEffectInAssertError(const Token *tok, const std::string& functionName) { reportError(tok, Severity::warning, "assertWithSideEffect", @@ -111,7 +126,7 @@ void CheckAssert::sideEffectInAssertError(const Token *tok, const std::string& f "builds, this is a bug.", CWE398, Certainty::normal); } -void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& varname) +void CheckAssertImpl::assignmentInAssertError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "assignmentInAssert", @@ -124,7 +139,7 @@ void CheckAssert::assignmentInAssertError(const Token *tok, const std::string& v } // checks if side effects happen on the variable prior to tmp -void CheckAssert::checkVariableAssignment(const Token* assignTok, const Scope *assertionScope) +void CheckAssertImpl::checkVariableAssignment(const Token* assignTok, const Scope *assertionScope) { if (!assignTok->isAssignmentOp() && assignTok->tokType() != Token::eIncDecOp) return; @@ -152,8 +167,21 @@ void CheckAssert::checkVariableAssignment(const Token* assignTok, const Scope *a // TODO: function calls on var } -bool CheckAssert::inSameScope(const Token* returnTok, const Token* assignTok) +bool CheckAssertImpl::inSameScope(const Token* returnTok, const Token* assignTok) { // TODO: even if a return is in the same scope, the assignment might not affect it. return returnTok->scope() == assignTok->scope(); } + +void CheckAssert::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + CheckAssertImpl checkAssert(&tokenizer, tokenizer.getSettings(), errorLogger); + checkAssert.assertWithSideEffects(); +} + +void CheckAssert::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + CheckAssertImpl c(nullptr, settings, errorLogger); + c.sideEffectInAssertError(nullptr, "function"); + c.assignmentInAssertError(nullptr, "var"); +} diff --git a/lib/checkassert.h b/lib/checkassert.h index a9693dff97a..0352578aa28 100644 --- a/lib/checkassert.h +++ b/lib/checkassert.h @@ -24,14 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; -class Scope; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -42,35 +40,13 @@ class Token; class CPPCHECKLIB CheckAssert : public Check { public: - CheckAssert() : Check(myName()) {} + CheckAssert() : Check("Assert") {} private: - CheckAssert(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** run checks, the token list is not simplified */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckAssert checkAssert(&tokenizer, tokenizer.getSettings(), errorLogger); - checkAssert.assertWithSideEffects(); - } - - void assertWithSideEffects(); - - void checkVariableAssignment(const Token* assignTok, const Scope *assertionScope); - static bool inSameScope(const Token* returnTok, const Token* assignTok); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void sideEffectInAssertError(const Token *tok, const std::string& functionName); - void assignmentInAssertError(const Token *tok, const std::string &varname); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckAssert c(nullptr, settings, errorLogger); - c.sideEffectInAssertError(nullptr, "function"); - c.assignmentInAssertError(nullptr, "var"); - } - - static std::string myName() { - return "Assert"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Warn if there are side effects in assert statements (since this cause different behaviour in debug/release builds).\n"; diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 895ee5387d1..81bff714804 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -23,6 +23,7 @@ #include "checkautovariables.h" #include "astutils.h" +#include "checkimpl.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" @@ -39,15 +40,55 @@ //--------------------------------------------------------------------------- +static const CWE CWE398(398U); // Indicator of Poor Code Quality +static const CWE CWE562(562U); // Return of Stack Variable Address +static const CWE CWE590(590U); // Free of Memory not on the Heap // Register this check class into cppcheck by creating a static instance of it.. namespace { CheckAutoVariables instance; -} -static const CWE CWE398(398U); // Indicator of Poor Code Quality -static const CWE CWE562(562U); // Return of Stack Variable Address -static const CWE CWE590(590U); // Free of Memory not on the Heap + class CheckAutoVariablesImpl : public CheckImpl + { + public: + /** This constructor is used when running checks. */ + CheckAutoVariablesImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** assign function argument */ + void assignFunctionArg(); + + /** Check auto variables */ + void autoVariables(); + + /** + * Check variable assignment.. value must be changed later or there will be a error reported + * @return true if error is reported */ + bool checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken = nullptr); + + void checkVarLifetime(); + void checkVarLifetimeScope(const Token * start, const Token * end); + + void errorAutoVariableAssignment(const Token *tok, bool inconclusive); + void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); + void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); + void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); + void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok); + void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); + void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); + void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); + void errorUselessAssignmentArg(const Token *tok); + void errorUselessAssignmentPtrArg(const Token *tok); + + private: + /** returns true if tokvalue has already been diagnosed */ + bool diag(const Token* tokvalue); + + std::set mDiagDanglingTemp; + }; +} static bool isPtrArg(const Token *tok) { @@ -206,7 +247,7 @@ static bool variableIsUsedInScope(const Token* start, nonneg int varId, const Sc return false; } -void CheckAutoVariables::assignFunctionArg() +void CheckAutoVariablesImpl::assignFunctionArg() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); @@ -261,7 +302,7 @@ static bool hasOverloadedAssignment(const Token* tok, bool c, bool& inconclusive return true; } -void CheckAutoVariables::autoVariables() +void CheckAutoVariablesImpl::autoVariables() { logChecker("CheckAutoVariables::autoVariables"); @@ -323,7 +364,7 @@ void CheckAutoVariables::autoVariables() } } -bool CheckAutoVariables::checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken) +bool CheckAutoVariablesImpl::checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken) { if (!startToken) startToken = Token::findsimplematch(expr, "=")->next(); @@ -364,7 +405,7 @@ bool CheckAutoVariables::checkAutoVariableAssignment(const Token *expr, bool inc //--------------------------------------------------------------------------- -void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inconclusive) +void CheckAutoVariablesImpl::errorAutoVariableAssignment(const Token *tok, bool inconclusive) { if (!inconclusive) { reportError(tok, Severity::error, "autoVariables", @@ -385,7 +426,7 @@ void CheckAutoVariables::errorAutoVariableAssignment(const Token *tok, bool inco } } -void CheckAutoVariables::errorUselessAssignmentArg(const Token *tok) +void CheckAutoVariablesImpl::errorUselessAssignmentArg(const Token *tok) { reportError(tok, Severity::style, @@ -393,7 +434,7 @@ void CheckAutoVariables::errorUselessAssignmentArg(const Token *tok) "Assignment of function parameter has no effect outside the function.", CWE398, Certainty::normal); } -void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) +void CheckAutoVariablesImpl::errorUselessAssignmentPtrArg(const Token *tok) { reportError(tok, Severity::warning, @@ -401,7 +442,7 @@ void CheckAutoVariables::errorUselessAssignmentPtrArg(const Token *tok) "Assignment of function parameter has no effect outside the function. Did you forget dereferencing it?", CWE398, Certainty::normal); } -bool CheckAutoVariables::diag(const Token* tokvalue) +bool CheckAutoVariablesImpl::diag(const Token* tokvalue) { if (!tokvalue) return true; @@ -538,7 +579,7 @@ static bool isAssignedToNonLocal(const Token* tok) return !var->isLocal() || var->isStatic(); } -void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) +void CheckAutoVariablesImpl::checkVarLifetimeScope(const Token * start, const Token * end) { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); if (!start) @@ -678,7 +719,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token } } -void CheckAutoVariables::checkVarLifetime() +void CheckAutoVariablesImpl::checkVarLifetime() { logChecker("CheckAutoVariables::checkVarLifetime"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -689,7 +730,7 @@ void CheckAutoVariables::checkVarLifetime() } } -void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) +void CheckAutoVariablesImpl::errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); @@ -698,7 +739,7 @@ void CheckAutoVariables::errorReturnDanglingLifetime(const Token *tok, const Val reportError(errorPath, Severity::error, "returnDanglingLifetime", msg + " that will be invalid when returning.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) +void CheckAutoVariablesImpl::errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); @@ -707,7 +748,7 @@ void CheckAutoVariables::errorInvalidLifetime(const Token *tok, const ValueFlow: reportError(errorPath, Severity::error, "invalidLifetime", msg + " that is out of scope.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok) +void CheckAutoVariablesImpl::errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); @@ -722,7 +763,7 @@ void CheckAutoVariables::errorDanglingTemporaryLifetime(const Token* tok, const inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) +void CheckAutoVariablesImpl::errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val) { const bool inconclusive = val ? val->isInconclusive() : false; ErrorPath errorPath = val ? val->errorPath : ErrorPath(); @@ -732,21 +773,21 @@ void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow: reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +void CheckAutoVariablesImpl::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +void CheckAutoVariablesImpl::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnReference", "Reference to local variable returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath) +void CheckAutoVariablesImpl::errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath) { std::string tokName = tok ? tok->str() : "x"; std::string varName = var ? var->name() : "y"; @@ -755,14 +796,14 @@ void CheckAutoVariables::errorDanglingReference(const Token *tok, const Variable reportError(errorPath, Severity::error, "danglingReference", msg, CWE562, Certainty::normal); } -void CheckAutoVariables::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +void CheckAutoVariablesImpl::errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); reportError( errorPath, Severity::error, "returnTempReference", "Reference to temporary returned.", CWE562, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) +void CheckAutoVariablesImpl::errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val) { const Variable *var = val ? val->tokvalue->variable() : (tok ? tok->variable() : nullptr); @@ -788,3 +829,29 @@ void CheckAutoVariables::errorInvalidDeallocation(const Token *tok, const ValueF "The deallocation of " + type + " results in undefined behaviour. You should only free memory " "that has been allocated dynamically.", CWE590, Certainty::normal); } + +void CheckAutoVariables::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + CheckAutoVariablesImpl checkAutoVariables(&tokenizer, tokenizer.getSettings(), errorLogger); + checkAutoVariables.assignFunctionArg(); + checkAutoVariables.checkVarLifetime(); + checkAutoVariables.autoVariables(); +} + +void CheckAutoVariables::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + ErrorPath errorPath; + CheckAutoVariablesImpl c(nullptr,settings,errorLogger); + c.errorAutoVariableAssignment(nullptr, false); + c.errorReturnReference(nullptr, errorPath, false); + c.errorDanglingReference(nullptr, nullptr, errorPath); + c.errorReturnTempReference(nullptr, errorPath, false); + c.errorDanglingTempReference(nullptr, errorPath, false); + c.errorInvalidDeallocation(nullptr, nullptr); + c.errorUselessAssignmentArg(nullptr); + c.errorUselessAssignmentPtrArg(nullptr); + c.errorReturnDanglingLifetime(nullptr, nullptr); + c.errorInvalidLifetime(nullptr, nullptr); + c.errorDanglngLifetime(nullptr, nullptr); + c.errorDanglingTemporaryLifetime(nullptr, nullptr, nullptr); +} diff --git a/lib/checkautovariables.h b/lib/checkautovariables.h index 236ab1f7d07..021fad15c1a 100644 --- a/lib/checkautovariables.h +++ b/lib/checkautovariables.h @@ -25,19 +25,11 @@ #include "check.h" #include "config.h" #include "errortypes.h" -#include "tokenize.h" #include -#include class Settings; -class Token; class ErrorLogger; -class Variable; - -namespace ValueFlow { - class Value; -} /// @addtogroup Checks /** @brief Various small checks for automatic variables */ @@ -47,69 +39,13 @@ namespace ValueFlow { class CPPCHECKLIB CheckAutoVariables : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckAutoVariables() : Check(myName()) {} + CheckAutoVariables() : Check("Auto Variables") {} private: - /** This constructor is used when running checks. */ - CheckAutoVariables(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckAutoVariables checkAutoVariables(&tokenizer, tokenizer.getSettings(), errorLogger); - checkAutoVariables.assignFunctionArg(); - checkAutoVariables.checkVarLifetime(); - checkAutoVariables.autoVariables(); - } - - /** assign function argument */ - void assignFunctionArg(); - - /** Check auto variables */ - void autoVariables(); - - /** - * Check variable assignment.. value must be changed later or there will be a error reported - * @return true if error is reported */ - bool checkAutoVariableAssignment(const Token *expr, bool inconclusive, const Token *startToken = nullptr); - - void checkVarLifetime(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void checkVarLifetimeScope(const Token * start, const Token * end); - - void errorAutoVariableAssignment(const Token *tok, bool inconclusive); - void errorReturnDanglingLifetime(const Token *tok, const ValueFlow::Value* val); - void errorInvalidLifetime(const Token *tok, const ValueFlow::Value* val); - void errorDanglngLifetime(const Token *tok, const ValueFlow::Value *val); - void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val, const Token* tempTok); - void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); - void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); - void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); - void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); - void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); - void errorUselessAssignmentArg(const Token *tok); - void errorUselessAssignmentPtrArg(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - ErrorPath errorPath; - CheckAutoVariables c(nullptr,settings,errorLogger); - c.errorAutoVariableAssignment(nullptr, false); - c.errorReturnReference(nullptr, errorPath, false); - c.errorDanglingReference(nullptr, nullptr, errorPath); - c.errorReturnTempReference(nullptr, errorPath, false); - c.errorDanglingTempReference(nullptr, errorPath, false); - c.errorInvalidDeallocation(nullptr, nullptr); - c.errorUselessAssignmentArg(nullptr); - c.errorUselessAssignmentPtrArg(nullptr); - c.errorReturnDanglingLifetime(nullptr, nullptr); - c.errorInvalidLifetime(nullptr, nullptr); - c.errorDanglngLifetime(nullptr, nullptr); - c.errorDanglingTemporaryLifetime(nullptr, nullptr, nullptr); - } - - static std::string myName() { - return "Auto Variables"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "A pointer to a variable is only valid as long as the variable is in scope.\n" @@ -121,11 +57,6 @@ class CPPCHECKLIB CheckAutoVariables : public Check { "- suspicious assignment of pointer argument\n" "- useless assignment of function argument\n"; } - - /** returns true if tokvalue has already been diagnosed */ - bool diag(const Token* tokvalue); - - std::set mDiagDanglingTemp; }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkbool.cpp b/lib/checkbool.cpp index 4de26ea2801..e7be5ba5f03 100644 --- a/lib/checkbool.cpp +++ b/lib/checkbool.cpp @@ -21,6 +21,7 @@ #include "checkbool.h" #include "astutils.h" +#include "checkimpl.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" @@ -32,23 +33,75 @@ #include //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckBool instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE571(571U); // Expression is Always True static const CWE CWE587(587U); // Assignment of a Fixed Address to a Pointer static const CWE CWE704(704U); // Incorrect Type Conversion or Cast +// Register this check class (by creating a static instance of it) +namespace { + CheckBool instance; + + class CheckBoolImpl: public CheckImpl + { + public: + /** @brief This constructor is used when running checks. */ + CheckBoolImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for comparison of function returning bool*/ + void checkComparisonOfFuncReturningBool(); + + /** @brief %Check for comparison of variable of type bool*/ + void checkComparisonOfBoolWithBool(); + + /** @brief %Check for using postfix increment on bool */ + void checkIncrementBoolean(); + + /** @brief %Check for suspicious comparison of a bool and a non-zero (and non-one) value (e.g. "if (!x==4)") */ + void checkComparisonOfBoolWithInt(); + + /** @brief assigning bool to pointer */ + void checkAssignBoolToPointer(); + + /** @brief assigning bool to float */ + void checkAssignBoolToFloat(); + + /** @brief %Check for using bool in bitwise expression */ + void checkBitwiseOnBoolean(); + + /** @brief %Check for comparing a bool expression with an integer other than 0 or 1 */ + void checkComparisonOfBoolExpressionWithInt(); + + /** @brief %Check for 'if (p+1)' etc. either somebody forgot to dereference, or else somebody uses pointer overflow */ + void pointerArithBool(); + void pointerArithBoolCond(const Token *tok); + + /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ + void returnValueOfFunctionReturningBool(); + + // Error messages.. + void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); + void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2); + void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression); + void incrementBooleanError(const Token *tok); + void comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression); + void assignBoolToPointerError(const Token *tok); + void assignBoolToFloatError(const Token *tok); + void bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound = false); + void comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1); + void pointerArithBoolError(const Token *tok); + void returnValueBoolError(const Token *tok); + }; +} + static bool isBool(const Variable* var) { return (var && Token::Match(var->typeEndToken(), "bool|_Bool")); } //--------------------------------------------------------------------------- -void CheckBool::checkIncrementBoolean() +void CheckBoolImpl::checkIncrementBoolean() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -65,7 +118,7 @@ void CheckBool::checkIncrementBoolean() } } -void CheckBool::incrementBooleanError(const Token *tok) +void CheckBoolImpl::incrementBooleanError(const Token *tok) { reportError( tok, @@ -88,7 +141,7 @@ static bool isConvertedToBool(const Token* tok) // if (bool & bool) -> if (bool && bool) // if (bool | bool) -> if (bool || bool) //--------------------------------------------------------------------------- -void CheckBool::checkBitwiseOnBoolean() +void CheckBoolImpl::checkBitwiseOnBoolean() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -131,7 +184,7 @@ void CheckBool::checkBitwiseOnBoolean() } } -void CheckBool::bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound) +void CheckBoolImpl::bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound) { std::string msg = "Boolean expression '" + expression + "' is used in bitwise operation."; if (!isCompound) @@ -148,7 +201,7 @@ void CheckBool::bitwiseOnBooleanError(const Token* tok, const std::string& expre // if (!x==3) <- Probably meant to be "x!=3" //--------------------------------------------------------------------------- -void CheckBool::checkComparisonOfBoolWithInt() +void CheckBoolImpl::checkComparisonOfBoolWithInt() { if (!mSettings->severity.isEnabled(Severity::warning) || !mTokenizer->isCPP()) return; @@ -175,7 +228,7 @@ void CheckBool::checkComparisonOfBoolWithInt() } } -void CheckBool::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) +void CheckBoolImpl::comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression) { reportError(tok, Severity::warning, "comparisonOfBoolWithInvalidComparator", "Comparison of a boolean value using relational operator (<, >, <= or >=).\n" @@ -199,7 +252,7 @@ static bool tokenIsFunctionReturningBool(const Token* tok) return false; } -void CheckBool::checkComparisonOfFuncReturningBool() +void CheckBoolImpl::checkComparisonOfFuncReturningBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -243,7 +296,7 @@ void CheckBool::checkComparisonOfFuncReturningBool() } } -void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) +void CheckBoolImpl::comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfFuncReturningBoolError", "Comparison of a function returning boolean value using relational (<, >, <= or >=) operator.\n" @@ -252,7 +305,7 @@ void CheckBool::comparisonOfFuncReturningBoolError(const Token *tok, const std:: " operator could cause unexpected results.", CWE398, Certainty::normal); } -void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) +void CheckBoolImpl::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2) { reportError(tok, Severity::style, "comparisonOfTwoFuncsReturningBoolError", "Comparison of two functions returning boolean value using relational (<, >, <= or >=) operator.\n" @@ -265,7 +318,7 @@ void CheckBool::comparisonOfTwoFuncsReturningBoolError(const Token *tok, const s // Comparison of bool with bool //------------------------------------------------------------------------------- -void CheckBool::checkComparisonOfBoolWithBool() +void CheckBoolImpl::checkComparisonOfBoolWithBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -306,7 +359,7 @@ void CheckBool::checkComparisonOfBoolWithBool() } } -void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) +void CheckBoolImpl::comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression) { reportError(tok, Severity::style, "comparisonOfBoolWithBoolError", "Comparison of a variable having boolean value using relational (<, >, <= or >=) operator.\n" @@ -316,7 +369,7 @@ void CheckBool::comparisonOfBoolWithBoolError(const Token *tok, const std::strin } //----------------------------------------------------------------------------- -void CheckBool::checkAssignBoolToPointer() +void CheckBoolImpl::checkAssignBoolToPointer() { logChecker("CheckBool::checkAssignBoolToPointer"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -329,7 +382,7 @@ void CheckBool::checkAssignBoolToPointer() } } -void CheckBool::assignBoolToPointerError(const Token *tok) +void CheckBoolImpl::assignBoolToPointerError(const Token *tok) { reportError(tok, Severity::error, "assignBoolToPointer", "Boolean value assigned to pointer.", CWE587, Certainty::normal); @@ -337,7 +390,7 @@ void CheckBool::assignBoolToPointerError(const Token *tok) //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void CheckBool::checkComparisonOfBoolExpressionWithInt() +void CheckBoolImpl::checkComparisonOfBoolExpressionWithInt() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -397,7 +450,7 @@ void CheckBool::checkComparisonOfBoolExpressionWithInt() } } -void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1) +void CheckBoolImpl::comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1) { if (not0or1) reportError(tok, Severity::warning, "compareBoolExpressionWithInt", @@ -408,7 +461,7 @@ void CheckBool::comparisonOfBoolExpressionWithIntError(const Token *tok, bool no } -void CheckBool::pointerArithBool() +void CheckBoolImpl::pointerArithBool() { logChecker("CheckBool::pointerArithBool"); @@ -431,7 +484,7 @@ void CheckBool::pointerArithBool() } } -void CheckBool::pointerArithBoolCond(const Token *tok) +void CheckBoolImpl::pointerArithBoolCond(const Token *tok) { if (!tok) return; @@ -451,7 +504,7 @@ void CheckBool::pointerArithBoolCond(const Token *tok) pointerArithBoolError(tok); } -void CheckBool::pointerArithBoolError(const Token *tok) +void CheckBoolImpl::pointerArithBoolError(const Token *tok) { reportError(tok, Severity::error, @@ -460,7 +513,7 @@ void CheckBool::pointerArithBoolError(const Token *tok) "Converting pointer arithmetic result to bool. The boolean result is always true unless there is pointer arithmetic overflow, and overflow is undefined behaviour. Probably a dereference is forgotten.", CWE571, Certainty::normal); } -void CheckBool::checkAssignBoolToFloat() +void CheckBoolImpl::checkAssignBoolToFloat() { if (!mTokenizer->isCPP()) return; @@ -477,13 +530,13 @@ void CheckBool::checkAssignBoolToFloat() } } -void CheckBool::assignBoolToFloatError(const Token *tok) +void CheckBoolImpl::assignBoolToFloatError(const Token *tok) { reportError(tok, Severity::style, "assignBoolToFloat", "Boolean value assigned to floating point variable.", CWE704, Certainty::normal); } -void CheckBool::returnValueOfFunctionReturningBool() +void CheckBoolImpl::returnValueOfFunctionReturningBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -511,7 +564,39 @@ void CheckBool::returnValueOfFunctionReturningBool() } } -void CheckBool::returnValueBoolError(const Token *tok) +void CheckBoolImpl::returnValueBoolError(const Token *tok) { reportError(tok, Severity::style, "returnNonBoolInBooleanFunction", "Non-boolean value returned from function returning bool"); } + +void CheckBool::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + CheckBoolImpl checkBool(&tokenizer, tokenizer.getSettings(), errorLogger); + + checkBool.checkComparisonOfBoolExpressionWithInt(); + checkBool.checkComparisonOfBoolWithInt(); + checkBool.checkAssignBoolToFloat(); + checkBool.pointerArithBool(); + checkBool.returnValueOfFunctionReturningBool(); + checkBool.checkComparisonOfFuncReturningBool(); + checkBool.checkComparisonOfBoolWithBool(); + checkBool.checkIncrementBoolean(); + checkBool.checkAssignBoolToPointer(); + checkBool.checkBitwiseOnBoolean(); +} + +void CheckBool::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + CheckBoolImpl c(nullptr, settings, errorLogger); + c.assignBoolToPointerError(nullptr); + c.assignBoolToFloatError(nullptr); + c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); + c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); + c.comparisonOfBoolWithBoolError(nullptr, "var_name"); + c.incrementBooleanError(nullptr); + c.bitwiseOnBooleanError(nullptr, "expression", "&&"); + c.comparisonOfBoolExpressionWithIntError(nullptr, true); + c.pointerArithBoolError(nullptr); + c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); + c.returnValueBoolError(nullptr); +} diff --git a/lib/checkbool.h b/lib/checkbool.h index fa69b4e3e1c..2b59b5e4e89 100644 --- a/lib/checkbool.h +++ b/lib/checkbool.h @@ -24,13 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -41,92 +40,13 @@ class Token; class CPPCHECKLIB CheckBool : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckBool() : Check(myName()) {} + CheckBool() : Check("Boolean") {} private: - /** @brief This constructor is used when running checks. */ - CheckBool(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckBool checkBool(&tokenizer, tokenizer.getSettings(), errorLogger); - - // Checks - checkBool.checkComparisonOfBoolExpressionWithInt(); - checkBool.checkComparisonOfBoolWithInt(); - checkBool.checkAssignBoolToFloat(); - checkBool.pointerArithBool(); - checkBool.returnValueOfFunctionReturningBool(); - checkBool.checkComparisonOfFuncReturningBool(); - checkBool.checkComparisonOfBoolWithBool(); - checkBool.checkIncrementBoolean(); - checkBool.checkAssignBoolToPointer(); - checkBool.checkBitwiseOnBoolean(); - } - - /** @brief %Check for comparison of function returning bool*/ - void checkComparisonOfFuncReturningBool(); - - /** @brief %Check for comparison of variable of type bool*/ - void checkComparisonOfBoolWithBool(); - - /** @brief %Check for using postfix increment on bool */ - void checkIncrementBoolean(); - - /** @brief %Check for suspicious comparison of a bool and a non-zero (and non-one) value (e.g. "if (!x==4)") */ - void checkComparisonOfBoolWithInt(); - - /** @brief assigning bool to pointer */ - void checkAssignBoolToPointer(); - - /** @brief assigning bool to float */ - void checkAssignBoolToFloat(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check for using bool in bitwise expression */ - void checkBitwiseOnBoolean(); - - /** @brief %Check for comparing a bool expression with an integer other than 0 or 1 */ - void checkComparisonOfBoolExpressionWithInt(); - - /** @brief %Check for 'if (p+1)' etc. either somebody forgot to dereference, or else somebody uses pointer overflow */ - void pointerArithBool(); - void pointerArithBoolCond(const Token *tok); - - /** @brief %Check if a function returning bool returns an integer other than 0 or 1 */ - void returnValueOfFunctionReturningBool(); - - // Error messages.. - void comparisonOfFuncReturningBoolError(const Token *tok, const std::string &expression); - void comparisonOfTwoFuncsReturningBoolError(const Token *tok, const std::string &expression1, const std::string &expression2); - void comparisonOfBoolWithBoolError(const Token *tok, const std::string &expression); - void incrementBooleanError(const Token *tok); - void comparisonOfBoolWithInvalidComparator(const Token *tok, const std::string &expression); - void assignBoolToPointerError(const Token *tok); - void assignBoolToFloatError(const Token *tok); - void bitwiseOnBooleanError(const Token* tok, const std::string& expression, const std::string& op, bool isCompound = false); - void comparisonOfBoolExpressionWithIntError(const Token *tok, bool not0or1); - void pointerArithBoolError(const Token *tok); - void returnValueBoolError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckBool c(nullptr, settings, errorLogger); - c.assignBoolToPointerError(nullptr); - c.assignBoolToFloatError(nullptr); - c.comparisonOfFuncReturningBoolError(nullptr, "func_name"); - c.comparisonOfTwoFuncsReturningBoolError(nullptr, "func_name1", "func_name2"); - c.comparisonOfBoolWithBoolError(nullptr, "var_name"); - c.incrementBooleanError(nullptr); - c.bitwiseOnBooleanError(nullptr, "expression", "&&"); - c.comparisonOfBoolExpressionWithIntError(nullptr, true); - c.pointerArithBoolError(nullptr); - c.comparisonOfBoolWithInvalidComparator(nullptr, "expression"); - c.returnValueBoolError(nullptr); - } - - static std::string myName() { - return "Boolean"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Boolean type checks\n" diff --git a/lib/checkboost.cpp b/lib/checkboost.cpp index 4913a691fba..a1ddb1872b7 100644 --- a/lib/checkboost.cpp +++ b/lib/checkboost.cpp @@ -18,20 +18,35 @@ #include "checkboost.h" +#include "checkimpl.h" #include "errortypes.h" #include "symboldatabase.h" #include "token.h" +#include "tokenize.h" #include +static const CWE CWE664(664); + // Register this check class (by creating a static instance of it) namespace { CheckBoost instance; -} -static const CWE CWE664(664); + class CheckBoostImpl : public CheckImpl +{ +public: + /** This constructor is used when running checks. */ + CheckBoostImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for container modification while using the BOOST_FOREACH macro */ + void checkBoostForeachModification(); + + void boostForeachError(const Token *tok); +}; +} -void CheckBoost::checkBoostForeachModification() +void CheckBoostImpl::checkBoostForeachModification() { logChecker("CheckBoost::checkBoostForeachModification"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -58,9 +73,24 @@ void CheckBoost::checkBoostForeachModification() } } -void CheckBoost::boostForeachError(const Token *tok) +void CheckBoostImpl::boostForeachError(const Token *tok) { reportError(tok, Severity::error, "boostForeachError", "BOOST_FOREACH caches the end() iterator. It's undefined behavior if you modify the container inside.", CWE664, Certainty::normal ); } + +void CheckBoost::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + if (!tokenizer.isCPP()) + return; + + CheckBoostImpl checkBoost(&tokenizer, tokenizer.getSettings(), errorLogger); + checkBoost.checkBoostForeachModification(); +} + +void CheckBoost::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + CheckBoostImpl c(nullptr, settings, errorLogger); + c.boostForeachError(nullptr); +} diff --git a/lib/checkboost.h b/lib/checkboost.h index a868463fe08..cd57255d600 100644 --- a/lib/checkboost.h +++ b/lib/checkboost.h @@ -24,13 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -40,35 +39,13 @@ class Token; class CPPCHECKLIB CheckBoost : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckBoost() : Check(myName()) {} + CheckBoost() : Check("Boost usage") {} private: - /** This constructor is used when running checks. */ - CheckBoost(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (!tokenizer.isCPP()) - return; - - CheckBoost checkBoost(&tokenizer, tokenizer.getSettings(), errorLogger); - checkBoost.checkBoostForeachModification(); - } - - /** @brief %Check for container modification while using the BOOST_FOREACH macro */ - void checkBoostForeachModification(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void boostForeachError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckBoost c(nullptr, settings, errorLogger); - c.boostForeachError(nullptr); - } - - static std::string myName() { - return "Boost usage"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check for invalid usage of Boost:\n" diff --git a/lib/checkbufferoverrun.cpp b/lib/checkbufferoverrun.cpp index d9acf389218..9ea4e778c29 100644 --- a/lib/checkbufferoverrun.cpp +++ b/lib/checkbufferoverrun.cpp @@ -23,6 +23,7 @@ #include "checkbufferoverrun.h" #include "astutils.h" +#include "checkimpl.h" #include "errorlogger.h" #include "library.h" #include "mathlib.h" @@ -45,13 +46,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckBufferOverrun instance; -} - -//--------------------------------------------------------------------------- - // CWE ids used: static const CWE CWE131(131U); // Incorrect Calculation of Buffer Size static const CWE CWE170(170U); // Improper Null Termination @@ -64,6 +58,51 @@ static const CWE CWE_BUFFER_OVERRUN(788U); // Access of Memory Location After //--------------------------------------------------------------------------- +// Register this check class (by creating a static instance of it) +namespace { + CheckBufferOverrun instance; + + class CheckBufferOverrunImpl : public CheckImpl + { + public: + /** This constructor is used when running checks. */ + CheckBufferOverrunImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + void arrayIndex(); + void arrayIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes); + void negativeIndexError(const Token* tok, + const std::vector& dimensions, + const std::vector& indexes); + + void pointerArithmetic(); + void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue); + + void bufferOverflow(); + void bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty); + + void arrayIndexThenCheck(); + void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); + + void stringNotZeroTerminated(); + void terminateStrncpyError(const Token *tok, const std::string &varname); + + void argumentSize(); + void argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg); + + void negativeArraySize(); + void negativeArraySizeError(const Token* tok); + void negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value); // provide a negative value to memory allocation function + + void objectIndex(); + void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known); + + ValueFlow::Value getBufferSize(const Token *bufTok) const; + }; +} + static const ValueFlow::Value *getBufferSizeValue(const Token *tok) { const std::list &tokenValues = tok->values(); @@ -279,7 +318,7 @@ static std::vector getOverrunIndexValues(const Token* tok, return {}; } -void CheckBufferOverrun::arrayIndex() +void CheckBufferOverrunImpl::arrayIndex() { logChecker("CheckBufferOverrun::arrayIndex"); @@ -400,7 +439,7 @@ static std::string arrayIndexMessage(const Token* tok, return errmsg.str(); } -void CheckBufferOverrun::arrayIndexError(const Token* tok, +void CheckBufferOverrunImpl::arrayIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes) { @@ -429,7 +468,7 @@ void CheckBufferOverrun::arrayIndexError(const Token* tok, index->isInconclusive() ? Certainty::inconclusive : Certainty::normal); } -void CheckBufferOverrun::negativeIndexError(const Token* tok, +void CheckBufferOverrunImpl::negativeIndexError(const Token* tok, const std::vector& dimensions, const std::vector& indexes) { @@ -459,7 +498,7 @@ void CheckBufferOverrun::negativeIndexError(const Token* tok, //--------------------------------------------------------------------------- -void CheckBufferOverrun::pointerArithmetic() +void CheckBufferOverrunImpl::pointerArithmetic() { if (!mSettings->severity.isEnabled(Severity::portability)) return; @@ -523,7 +562,7 @@ void CheckBufferOverrun::pointerArithmetic() } } -void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue) +void CheckBufferOverrunImpl::pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue) { if (!tok) { reportError(tok, Severity::portability, "pointerOutOfBounds", "Pointer arithmetic overflow.", CWE_POINTER_ARITHMETIC_OVERFLOW, Certainty::normal); @@ -547,7 +586,7 @@ void CheckBufferOverrun::pointerArithmeticError(const Token *tok, const Token *i //--------------------------------------------------------------------------- -ValueFlow::Value CheckBufferOverrun::getBufferSize(const Token *bufTok) const +ValueFlow::Value CheckBufferOverrunImpl::getBufferSize(const Token *bufTok) const { if (!bufTok->valueType()) return ValueFlow::Value(-1); @@ -629,7 +668,7 @@ static bool checkBufferSize(const Token *ftok, const Library::ArgumentChecks::Mi } -void CheckBufferOverrun::bufferOverflow() +void CheckBufferOverrunImpl::bufferOverflow() { logChecker("CheckBufferOverrun::bufferOverflow"); @@ -686,14 +725,14 @@ void CheckBufferOverrun::bufferOverflow() } } -void CheckBufferOverrun::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty) +void CheckBufferOverrunImpl::bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty) { reportError(getErrorPath(tok, value, "Buffer overrun"), Severity::error, "bufferAccessOutOfBounds", "Buffer is accessed out of bounds: " + (tok ? tok->expressionString() : "buf"), CWE_BUFFER_OVERRUN, certainty); } //--------------------------------------------------------------------------- -void CheckBufferOverrun::arrayIndexThenCheck() +void CheckBufferOverrunImpl::arrayIndexThenCheck() { if (!mSettings->severity.isEnabled(Severity::portability)) return; @@ -737,7 +776,7 @@ void CheckBufferOverrun::arrayIndexThenCheck() } } -void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) +void CheckBufferOverrunImpl::arrayIndexThenCheckError(const Token *tok, const std::string &indexName) { reportError(tok, Severity::style, "arrayIndexThenCheck", "$symbol:" + indexName + "\n" @@ -750,7 +789,7 @@ void CheckBufferOverrun::arrayIndexThenCheckError(const Token *tok, const std::s //--------------------------------------------------------------------------- -void CheckBufferOverrun::stringNotZeroTerminated() +void CheckBufferOverrunImpl::stringNotZeroTerminated() { // this is currently 'inconclusive'. See TestBufferOverrun::terminateStrncpy3 if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) @@ -803,7 +842,7 @@ void CheckBufferOverrun::stringNotZeroTerminated() } } -void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::string &varname) +void CheckBufferOverrunImpl::terminateStrncpyError(const Token *tok, const std::string &varname) { const std::string shortMessage = "The buffer '$symbol' may not be null-terminated after the call to strncpy()."; reportError(tok, Severity::warning, "terminateStrncpy", @@ -816,7 +855,7 @@ void CheckBufferOverrun::terminateStrncpyError(const Token *tok, const std::stri } //--------------------------------------------------------------------------- -void CheckBufferOverrun::argumentSize() +void CheckBufferOverrunImpl::argumentSize() { // Check '%type% x[10]' arguments if (!mSettings->severity.isEnabled(Severity::warning)) @@ -864,7 +903,7 @@ void CheckBufferOverrun::argumentSize() } } -void CheckBufferOverrun::argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg) +void CheckBufferOverrunImpl::argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg) { const std::string strParamNum = std::to_string(paramIndex + 1) + getOrdinalText(paramIndex + 1); ErrorPath errorPath; @@ -980,9 +1019,8 @@ bool CheckBufferOverrun::analyseWholeProgram(const CTU::FileInfo *ctu, const std bool foundErrors = false; (void)settings; // This argument is unused - CheckBufferOverrun dummy(nullptr, &settings, &errorLogger); - dummy. - logChecker("CheckBufferOverrun::analyseWholeProgram"); + CheckBufferOverrunImpl dummy(nullptr, &settings, &errorLogger); + //dummy.logChecker("CheckBufferOverrun::analyseWholeProgram"); // TODO const std::map> callsMap = ctu->getCallsMap(); @@ -1040,7 +1078,7 @@ bool CheckBufferOverrun::analyseWholeProgram1(const std::mapgetSymbolDatabase(); @@ -1114,7 +1152,7 @@ void CheckBufferOverrun::objectIndex() } } -void CheckBufferOverrun::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known) +void CheckBufferOverrunImpl::objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known) { ErrorPath errorPath; std::string name; @@ -1148,7 +1186,7 @@ static bool isVLAIndex(const Token* tok) return isVLAIndex(tok->astOperand1()) || isVLAIndex(tok->astOperand2()); } -void CheckBufferOverrun::negativeArraySize() +void CheckBufferOverrunImpl::negativeArraySize() { logChecker("CheckBufferOverrun::negativeArraySize"); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -1178,7 +1216,7 @@ void CheckBufferOverrun::negativeArraySize() } } -void CheckBufferOverrun::negativeArraySizeError(const Token* tok) +void CheckBufferOverrunImpl::negativeArraySizeError(const Token* tok) { const std::string arrayName = tok ? tok->expressionString() : std::string(); const std::string line1 = arrayName.empty() ? std::string() : ("$symbol:" + arrayName + '\n'); @@ -1187,7 +1225,7 @@ void CheckBufferOverrun::negativeArraySizeError(const Token* tok) "Declaration of array '" + arrayName + "' with negative size is undefined behaviour", CWE758, Certainty::normal); } -void CheckBufferOverrun::negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value) +void CheckBufferOverrunImpl::negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value) { const std::string msg = "Memory allocation size is negative."; const ErrorPath errorPath = getErrorPath(tok, value, msg); @@ -1195,3 +1233,30 @@ void CheckBufferOverrun::negativeMemoryAllocationSizeError(const Token* tok, con reportError(errorPath, inconclusive ? Severity::warning : Severity::error, "negativeMemoryAllocationSize", msg, CWE131, inconclusive ? Certainty::inconclusive : Certainty::normal); } + +void CheckBufferOverrun::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + CheckBufferOverrunImpl checkBufferOverrun(&tokenizer, tokenizer.getSettings(), errorLogger); + checkBufferOverrun.arrayIndex(); + checkBufferOverrun.pointerArithmetic(); + checkBufferOverrun.bufferOverflow(); + checkBufferOverrun.arrayIndexThenCheck(); + checkBufferOverrun.stringNotZeroTerminated(); + checkBufferOverrun.objectIndex(); + checkBufferOverrun.argumentSize(); + checkBufferOverrun.negativeArraySize(); +} + +void CheckBufferOverrun::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + CheckBufferOverrunImpl c(nullptr, settings, errorLogger); + c.arrayIndexError(nullptr, std::vector(), std::vector()); + c.pointerArithmeticError(nullptr, nullptr, nullptr); + c.negativeIndexError(nullptr, std::vector(), std::vector()); + c.arrayIndexThenCheckError(nullptr, "i"); + c.bufferOverflowError(nullptr, nullptr, Certainty::normal); + c.objectIndexError(nullptr, nullptr, true); + c.argumentSizeError(nullptr, "function", 1, "buffer", nullptr, nullptr); + c.negativeMemoryAllocationSizeError(nullptr, nullptr); + c.negativeArraySizeError(nullptr); +} diff --git a/lib/checkbufferoverrun.h b/lib/checkbufferoverrun.h index 5ae2786e600..2fa7b4c942f 100644 --- a/lib/checkbufferoverrun.h +++ b/lib/checkbufferoverrun.h @@ -28,7 +28,6 @@ #include "errortypes.h" #include "mathlib.h" #include "symboldatabase.h" -#include "tokenize.h" #include "vfvalue.h" #include @@ -43,6 +42,7 @@ namespace tinyxml2 { class ErrorLogger; class Settings; class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -59,37 +59,10 @@ class Token; class CPPCHECKLIB CheckBufferOverrun : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckBufferOverrun() : Check(myName()) {} + CheckBufferOverrun() : Check("Bounds checking") {} private: - /** This constructor is used when running checks. */ - CheckBufferOverrun(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckBufferOverrun checkBufferOverrun(&tokenizer, tokenizer.getSettings(), errorLogger); - checkBufferOverrun.arrayIndex(); - checkBufferOverrun.pointerArithmetic(); - checkBufferOverrun.bufferOverflow(); - checkBufferOverrun.arrayIndexThenCheck(); - checkBufferOverrun.stringNotZeroTerminated(); - checkBufferOverrun.objectIndex(); - checkBufferOverrun.argumentSize(); - checkBufferOverrun.negativeArraySize(); - } - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckBufferOverrun c(nullptr, settings, errorLogger); - c.arrayIndexError(nullptr, std::vector(), std::vector()); - c.pointerArithmeticError(nullptr, nullptr, nullptr); - c.negativeIndexError(nullptr, std::vector(), std::vector()); - c.arrayIndexThenCheckError(nullptr, "i"); - c.bufferOverflowError(nullptr, nullptr, Certainty::normal); - c.objectIndexError(nullptr, nullptr, true); - c.argumentSizeError(nullptr, "function", 1, "buffer", nullptr, nullptr); - c.negativeMemoryAllocationSizeError(nullptr, nullptr); - c.negativeArraySizeError(nullptr); - } + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const override; @@ -97,39 +70,8 @@ class CPPCHECKLIB CheckBufferOverrun : public Check { /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; - void arrayIndex(); - void arrayIndexError(const Token* tok, - const std::vector& dimensions, - const std::vector& indexes); - void negativeIndexError(const Token* tok, - const std::vector& dimensions, - const std::vector& indexes); - - void pointerArithmetic(); - void pointerArithmeticError(const Token *tok, const Token *indexToken, const ValueFlow::Value *indexValue); - - void bufferOverflow(); - void bufferOverflowError(const Token *tok, const ValueFlow::Value *value, Certainty certainty); - - void arrayIndexThenCheck(); - void arrayIndexThenCheckError(const Token *tok, const std::string &indexName); - - void stringNotZeroTerminated(); - void terminateStrncpyError(const Token *tok, const std::string &varname); + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; - void argumentSize(); - void argumentSizeError(const Token *tok, const std::string &functionName, nonneg int paramIndex, const std::string ¶mExpression, const Variable *paramVar, const Variable *functionArg); - - void negativeArraySize(); - void negativeArraySizeError(const Token* tok); - void negativeMemoryAllocationSizeError(const Token* tok, const ValueFlow::Value* value); // provide a negative value to memory allocation function - - void objectIndex(); - void objectIndexError(const Token *tok, const ValueFlow::Value *v, bool known); - - ValueFlow::Value getBufferSize(const Token *bufTok) const; - - // CTU static bool isCtuUnsafeBufferUsage(const Settings *settings, const Token *argtok, MathLib::bigint *offset, int type); static bool isCtuUnsafeArrayIndex(const Settings *settings, const Token *argtok, MathLib::bigint *offset); static bool isCtuUnsafePointerArith(const Settings *settings, const Token *argtok, MathLib::bigint *offset); @@ -137,11 +79,6 @@ class CPPCHECKLIB CheckBufferOverrun : public Check { Check::FileInfo * loadFileInfoFromXml(const tinyxml2::XMLElement *xmlElement) const override; static bool analyseWholeProgram1(const std::map> &callsMap, const CTU::FileInfo::UnsafeUsage &unsafeUsage, int type, ErrorLogger &errorLogger); - - static std::string myName() { - return "Bounds checking"; - } - std::string classInfo() const override { return "Out of bounds checking:\n" "- Array index out of bounds\n" diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index c6005eca1ee..e3964950bd6 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -20,6 +20,7 @@ #include "checkclass.h" #include "astutils.h" +#include "checkimpl.h" #include "library.h" #include "settings.h" #include "standards.h" @@ -46,11 +47,6 @@ namespace CTU { //--------------------------------------------------------------------------- -// Register CheckClass.. -namespace { - CheckClass instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE404(404U); // Improper Resource Shutdown or Release static const CWE CWE665(665U); // Improper Initialization @@ -59,6 +55,249 @@ static const CWE CWE762(762U); // Mismatched Memory Management Routines static const CWE CWE_ONE_DEFINITION_RULE(758U); +// Register CheckClass.. +namespace { + CheckClass instance; + + class CheckClassImpl : public CheckImpl + { + public: + /** @brief This constructor is used when running checks. */ + CheckClassImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); + + /** @brief %Check that all class constructors are ok */ + void constructors(); + + /** @brief %Check that constructors with single parameter are explicit, + * if they has to be.*/ + void checkExplicitConstructors(); + + /** @brief %Check that all private functions are called */ + void privateFunctions(); + + /** + * @brief %Check that the memsets are valid. + * The 'memset' function can do dangerous things if used wrong. If it + * is used on STL containers for instance it will clear all its data + * and then the STL container may leak memory or worse have an invalid state. + * It can also overwrite the virtual table. + * Important: The checking doesn't work on simplified tokens list. + */ + void checkMemset(); + void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes); + + /** @brief 'operator=' should return reference to *this */ + void operatorEqRetRefThis(); // Warning upon no "return *this;" + + /** @brief 'operator=' should check for assignment to self */ + void operatorEqToSelf(); // Warning upon no check for assignment to self + + /** @brief The destructor in a base class should be virtual */ + void virtualDestructor(); + + /** @brief warn for "this-x". The indented code may be "this->x" */ + void thisSubtraction(); + + /** @brief can member function be const? */ + void checkConst(); + + /** @brief Check initializer list order */ + void initializerListOrder(); + + /** @brief Suggest using initialization list */ + void initializationListUsage(); + + /** @brief Check for initialization of a member with itself */ + void checkSelfInitialization(); + + void copyconstructors(); + + /** @brief call of virtual function in constructor/destructor */ + void checkVirtualFunctionCallInConstructor(); + + /** @brief Check duplicated inherited members */ + void checkDuplInheritedMembers(); + + /** @brief Check that copy constructor and operator defined together */ + void checkCopyCtorAndEqOperator(); + + /** @brief Check that the override keyword is used when overriding virtual functions */ + void checkOverride(); + + /** @brief Check that the overriden function is not identical to the base function */ + void checkUselessOverride(); + + /** @brief When "self pointer" is destroyed, 'this' might become invalid. */ + void checkThisUseAfterFree(); + + /** @brief Unsafe class check - const reference member */ + void checkUnsafeClassRefMember(); + + void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); + void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct); + //void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name); + void copyConstructorShallowCopyError(const Token *tok, const std::string& varname); + void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); + void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); + void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); + void uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive); + void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname); + void missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string& classname, const std::string& varname); + void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive); + void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); + void memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer = false); + void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type); + void memsetErrorFloat(const Token *tok, const std::string &type); + void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname); + void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok); + void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); + void thisSubtractionError(const Token *tok); + void operatorEqRetRefThisError(const Token *tok); + void operatorEqShouldBeLeftUnimplementedError(const Token *tok); + void operatorEqMissingReturnStatementError(const Token *tok, bool error); + void operatorEqToSelfError(const Token *tok); + void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); + void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); + void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); + void suggestInitializationList(const Token *tok, const std::string& varname); + void selfInitializationError(const Token* tok, const std::string& varname); + void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); + void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); + void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction = false); + void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); + void overrideError(const Function *funcInBase, const Function *funcInDerived); + void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode = false); + void thisUseAfterFree(const Token *self, const Token *free, const Token *use); + void unsafeClassRefMemberError(const Token *tok, const std::string &varname); + void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase); + + const SymbolDatabase* mSymbolDatabase{}; + + // operatorEqRetRefThis helper functions + void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last); + void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions); + + // operatorEqToSelf helper functions + bool hasAllocation(const Function *func, const Scope* scope) const; + bool hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const; + bool hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const; + static bool hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart); + enum class Bool { TRUE, FALSE, BAILOUT }; + static Bool isInverted(const Token *tok, const Token *rhs); + static const Token * getIfStmtBodyStart(const Token *tok, const Token *rhs); + + // checkConst helper functions + bool isMemberVar(const Scope *scope, const Token *tok) const; + static bool isMemberFunc(const Scope *scope, const Token *tok); + static bool isConstMemberFunc(const Scope *scope, const Token *tok); + enum class MemberAccess { NONE, SELF, MEMBER }; + bool checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const; + + // constructors helper function + /** @brief Information about a member variable. Used when checking for uninitialized variables */ + struct Usage { + explicit Usage(const Variable *var) : var(var) {} + + /** Variable that this usage is for */ + const Variable *var; + + /** @brief has this variable been assigned? */ + bool assign{}; + + /** @brief has this variable been initialized? */ + bool init{}; + }; + + static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope); + + /** + * @brief Create usage list that contains all scope members and also members + * of base classes without constructors. + * @param scope current class scope + */ + static std::vector createUsageList(const Scope *scope); + + /** + * @brief assign a variable in the varlist + * @param usageList reference to usage vector + * @param varid id of variable to mark assigned + */ + static void assignVar(std::vector &usageList, nonneg int varid); + + /** + * @brief assign a variable in the varlist + * @param usageList reference to usage vector + * @param vartok variable token + */ + static void assignVar(std::vector &usageList, const Token *vartok); + + /** + * @brief initialize a variable in the varlist + * @param usageList reference to usage vector + * @param varid id of variable to mark initialized + */ + static void initVar(std::vector &usageList, nonneg int varid); + + /** + * @brief set all variables in list assigned + * @param usageList reference to usage vector + */ + static void assignAllVar(std::vector &usageList); + + /** + * @brief set all variable in list assigned, if visible from given scope + * @param usageList reference to usage vector + * @param scope scope from which usages must be visible + */ + static void assignAllVarsVisibleFromScope(std::vector &usageList, const Scope *scope); + + /** + * @brief set all variables in list not assigned and not initialized + * @param usageList reference to usage vector + */ + static void clearAllVar(std::vector &usageList); + + /** + * @brief parse a scope for a constructor or member function and set the "init" flags in the provided varlist + * @param func reference to the function that should be checked + * @param callstack the function doesn't look into recursive function calls. + * @param scope pointer to variable Scope + * @param usage reference to usage vector + */ + void initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) const; + + /** + * @brief gives a list of tokens where virtual functions are called directly or indirectly + * @param function function to be checked + * @param virtualFunctionCallsMap map of results for already checked functions + * @return list of tokens where pure virtual functions are called + */ + const std::list & getVirtualFunctionCalls( + const Function & function, + std::map> & virtualFunctionCallsMap); + + /** + * @brief looks for the first virtual function call stack + * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls + * @param callToken token where pure virtual function is called directly or indirectly + * @param[in,out] pureFuncStack list to append the stack + */ + static void getFirstVirtualFunctionCallStack( + std::map> & virtualFunctionCallsMap, + const Token *callToken, + std::list & pureFuncStack); + + static bool canNotCopy(const Scope *scope); + + static bool canNotMove(const Scope *scope); + + /** + * @brief Helper for checkThisUseAfterFree + */ + bool checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken); + }; +} + static const char * getFunctionTypeName(Function::Type type) { switch (type) { @@ -114,8 +353,8 @@ static bool isVclTypeInit(const Type *type) } //--------------------------------------------------------------------------- -CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger), +CheckClassImpl::CheckClassImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger), mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) {} @@ -123,7 +362,7 @@ CheckClass::CheckClass(const Tokenizer *tokenizer, const Settings *settings, Err // ClassCheck: Check that all class constructors are ok. //--------------------------------------------------------------------------- -void CheckClass::constructors() +void CheckClassImpl::constructors() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); @@ -334,7 +573,7 @@ void CheckClass::constructors() } } -void CheckClass::checkExplicitConstructors() +void CheckClassImpl::checkExplicitConstructors() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -403,7 +642,7 @@ static bool hasNonCopyableBase(const Scope *scope, bool *unknown) return false; } -void CheckClass::copyconstructors() +void CheckClassImpl::copyconstructors() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -535,7 +774,7 @@ void CheckClass::copyconstructors() } */ -void CheckClass::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) +void CheckClassImpl::copyConstructorShallowCopyError(const Token *tok, const std::string& varname) { reportError(tok, Severity::warning, "copyCtorPointerCopying", "$symbol:" + varname + "\nValue of pointer '$symbol', which points to allocated memory, is copied in copy constructor instead of allocating new memory.", CWE398, Certainty::normal); @@ -561,22 +800,22 @@ static std::string noMemberErrorMessage(const Scope *scope, const char function[ return errmsg; } -void CheckClass::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) +void CheckClassImpl::noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noCopyConstructor", noMemberErrorMessage(scope, "copy constructor", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckClass::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) +void CheckClassImpl::noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive) { reportError(alloc, Severity::warning, "noOperatorEq", noMemberErrorMessage(scope, "operator=", isdefault), CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckClass::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc) +void CheckClassImpl::noDestructorError(const Scope *scope, bool isdefault, const Token *alloc) { reportError(alloc, Severity::warning, "noDestructor", noMemberErrorMessage(scope, "destructor", isdefault), CWE398, Certainty::normal); } -bool CheckClass::canNotCopy(const Scope *scope) +bool CheckClassImpl::canNotCopy(const Scope *scope) { bool constructor = false; bool publicAssign = false; @@ -600,7 +839,7 @@ bool CheckClass::canNotCopy(const Scope *scope) return constructor && !(publicAssign || publicCopy); } -bool CheckClass::canNotMove(const Scope *scope) +bool CheckClassImpl::canNotMove(const Scope *scope) { bool constructor = false; bool publicAssign = false; @@ -645,7 +884,7 @@ static void getAllVariableMembers(const Scope *scope, std::vector CheckClass::createUsageList(const Scope *scope) +std::vector CheckClassImpl::createUsageList(const Scope *scope) { std::vector ret; std::vector varlist; @@ -657,7 +896,7 @@ std::vector CheckClass::createUsageList(const Scope *scope) return ret; } -void CheckClass::assignVar(std::vector &usageList, nonneg int varid) +void CheckClassImpl::assignVar(std::vector &usageList, nonneg int varid) { auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) { return usage.var->declarationId() == varid; @@ -666,7 +905,7 @@ void CheckClass::assignVar(std::vector &usageList, nonneg int varid) it->assign = true; } -void CheckClass::assignVar(std::vector &usageList, const Token* vartok) +void CheckClassImpl::assignVar(std::vector &usageList, const Token* vartok) { if (vartok->varId() > 0) { assignVar(usageList, vartok->varId()); @@ -680,7 +919,7 @@ void CheckClass::assignVar(std::vector &usageList, const Token* vartok) it->assign = true; } -void CheckClass::initVar(std::vector &usageList, nonneg int varid) +void CheckClassImpl::initVar(std::vector &usageList, nonneg int varid) { auto it = std::find_if(usageList.begin(), usageList.end(), [varid](const Usage& usage) { return usage.var->declarationId() == varid; @@ -689,13 +928,13 @@ void CheckClass::initVar(std::vector &usageList, nonneg int varid) it->init = true; } -void CheckClass::assignAllVar(std::vector &usageList) +void CheckClassImpl::assignAllVar(std::vector &usageList) { for (Usage & i : usageList) i.assign = true; } -void CheckClass::assignAllVarsVisibleFromScope(std::vector& usageList, const Scope* scope) +void CheckClassImpl::assignAllVarsVisibleFromScope(std::vector& usageList, const Scope* scope) { for (Usage& usage : usageList) { if (usage.var->scope() == scope) @@ -711,7 +950,7 @@ void CheckClass::assignAllVarsVisibleFromScope(std::vector& usageList, co } } -void CheckClass::clearAllVar(std::vector &usageList) +void CheckClassImpl::clearAllVar(std::vector &usageList) { for (Usage & i : usageList) { i.assign = false; @@ -719,7 +958,7 @@ void CheckClass::clearAllVar(std::vector &usageList) } } -bool CheckClass::isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope) +bool CheckClassImpl::isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope) { // Iterate through each base class... for (const Type::BaseInfo & i : scope->definedType->derivedFrom) { @@ -746,7 +985,7 @@ bool CheckClass::isBaseClassMutableMemberFunc(const Token *tok, const Scope *sco return false; } -void CheckClass::initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) const +void CheckClassImpl::initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) const { if (!func.functionScope) return; @@ -1074,7 +1313,7 @@ void CheckClass::initializeVarList(const Function &func, std::listseverity.isEnabled(Severity::style)) return; @@ -1329,7 +1568,7 @@ void CheckClass::privateFunctions() } } -void CheckClass::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) +void CheckClassImpl::unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname) { reportError(tok, Severity::style, "unusedPrivateFunction", "$symbol:" + classname + "::" + funcname + "\nUnused private function: '$symbol'", CWE398, Certainty::normal); } @@ -1348,7 +1587,7 @@ static const Scope* findFunctionOf(const Scope* scope) return nullptr; } -void CheckClass::checkMemset() +void CheckClassImpl::checkMemset() { logChecker("CheckClass::checkMemset"); const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); @@ -1441,7 +1680,7 @@ void CheckClass::checkMemset() } } -void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes) +void CheckClassImpl::checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes) { // If type has been checked there is no need to check it again if (parsedTypes.find(type) != parsedTypes.end()) @@ -1507,7 +1746,7 @@ void CheckClass::checkMemsetType(const Scope *start, const Token *tok, const Sco } } -void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) +void CheckClassImpl::mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok) { std::list toks = { tok, classTok }; reportError(toks, Severity::warning, "mallocOnClassWarning", @@ -1517,7 +1756,7 @@ void CheckClass::mallocOnClassWarning(const Token* tok, const std::string &memfu "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE762, Certainty::normal); } -void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) +void CheckClassImpl::mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname) { std::list toks = { tok, classTok }; reportError(toks, Severity::error, "mallocOnClassError", @@ -1528,7 +1767,7 @@ void CheckClass::mallocOnClassError(const Token* tok, const std::string &memfunc "since no constructor is called and class members remain uninitialized. Consider using 'new' instead.", CWE665, Certainty::normal); } -void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer) +void CheckClassImpl::memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer) { const std::string typeStr = isContainer ? std::string() : (type + " that contains a "); const std::string msg = "$symbol:" + memfunc + "\n" @@ -1540,14 +1779,14 @@ void CheckClass::memsetError(const Token *tok, const std::string &memfunc, const reportError(tok, Severity::error, "memsetClass", msg, CWE762, Certainty::normal); } -void CheckClass::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) +void CheckClassImpl::memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type) { reportError(tok, Severity::error, "memsetClassReference", "$symbol:" + memfunc +"\n" "Using '" + memfunc + "' on " + type + " that contains a reference.", CWE665, Certainty::normal); } -void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type) +void CheckClassImpl::memsetErrorFloat(const Token *tok, const std::string &type) { reportError(tok, Severity::portability, "memsetClassFloat", "Using memset() on " + type + " which contains a floating point number.\n" "Using memset() on " + type + " which contains a floating point number." @@ -1562,7 +1801,7 @@ void CheckClass::memsetErrorFloat(const Token *tok, const std::string &type) // operator= should return a reference to *this //--------------------------------------------------------------------------- -void CheckClass::operatorEqRetRefThis() +void CheckClassImpl::operatorEqRetRefThis() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1581,13 +1820,13 @@ void CheckClass::operatorEqRetRefThis() } } -void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) +void CheckClassImpl::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last) { std::set analyzedFunctions; checkReturnPtrThis(scope, func, tok, last, analyzedFunctions); } -void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions) +void CheckClassImpl::checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions) { bool foundReturn = false; @@ -1673,17 +1912,17 @@ void CheckClass::checkReturnPtrThis(const Scope *scope, const Function *func, co operatorEqMissingReturnStatementError(func->token, func->access == AccessControl::Public); } -void CheckClass::operatorEqRetRefThisError(const Token *tok) +void CheckClassImpl::operatorEqRetRefThisError(const Token *tok) { reportError(tok, Severity::style, "operatorEqRetRefThis", "'operator=' should return reference to 'this' instance.", CWE398, Certainty::normal); } -void CheckClass::operatorEqShouldBeLeftUnimplementedError(const Token *tok) +void CheckClassImpl::operatorEqShouldBeLeftUnimplementedError(const Token *tok) { reportError(tok, Severity::style, "operatorEqShouldBeLeftUnimplemented", "'operator=' should either return reference to 'this' instance or be declared private and left unimplemented.", CWE398, Certainty::normal); } -void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool error) +void CheckClassImpl::operatorEqMissingReturnStatementError(const Token *tok, bool error) { if (error) { reportError(tok, Severity::error, "operatorEqMissingReturnStatement", "No 'return' statement in non-void function causes undefined behavior.", CWE398, Certainty::normal); @@ -1706,7 +1945,7 @@ void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool er // assignment to self. //--------------------------------------------------------------------------- -void CheckClass::operatorEqToSelf() +void CheckClassImpl::operatorEqToSelf() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -1747,7 +1986,7 @@ void CheckClass::operatorEqToSelf() } } -bool CheckClass::hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const +bool CheckClassImpl::hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const { const Token *end; if (ifStatementScopeStart->str() == "{") @@ -1757,12 +1996,12 @@ bool CheckClass::hasAllocationInIfScope(const Function *func, const Scope* scope return hasAllocation(func, scope, ifStatementScopeStart, end); } -bool CheckClass::hasAllocation(const Function *func, const Scope* scope) const +bool CheckClassImpl::hasAllocation(const Function *func, const Scope* scope) const { return hasAllocation(func, scope, func->functionScope->bodyStart, func->functionScope->bodyEnd); } -bool CheckClass::hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const +bool CheckClassImpl::hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const { if (!end) end = func->functionScope->bodyEnd; @@ -1808,7 +2047,7 @@ static bool isFalseKeyword(const Token* tok) * Checks if self-assignment test is inverse * For example 'if (this == &rhs)' */ -CheckClass::Bool CheckClass::isInverted(const Token *tok, const Token *rhs) +CheckClassImpl::Bool CheckClassImpl::isInverted(const Token *tok, const Token *rhs) { bool res = true; for (const Token *itr = tok; itr && itr->str()!="("; itr=itr->astParent()) { @@ -1837,7 +2076,7 @@ CheckClass::Bool CheckClass::isInverted(const Token *tok, const Token *rhs) return Bool::FALSE; } -const Token * CheckClass::getIfStmtBodyStart(const Token *tok, const Token *rhs) +const Token * CheckClassImpl::getIfStmtBodyStart(const Token *tok, const Token *rhs) { const Token *top = tok->astTop(); if (Token::simpleMatch(top->link(), ") {")) { @@ -1853,7 +2092,7 @@ const Token * CheckClass::getIfStmtBodyStart(const Token *tok, const Token *rhs) return nullptr; } -bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart) +bool CheckClassImpl::hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart) { if (!rhs) return false; @@ -1887,7 +2126,7 @@ bool CheckClass::hasAssignSelf(const Function *func, const Token *rhs, const Tok return false; } -void CheckClass::operatorEqToSelfError(const Token *tok) +void CheckClassImpl::operatorEqToSelfError(const Token *tok) { reportError(tok, Severity::warning, "operatorEqToSelf", "'operator=' should check for assignment to self to avoid problems with dynamic memory.\n" @@ -1899,7 +2138,7 @@ void CheckClass::operatorEqToSelfError(const Token *tok) // A destructor in a base class should be virtual //--------------------------------------------------------------------------- -void CheckClass::virtualDestructor() +void CheckClassImpl::virtualDestructor() { // This error should only be given if: // * base class doesn't have virtual destructor @@ -2034,7 +2273,7 @@ void CheckClass::virtualDestructor() virtualDestructorError(func->tokenDef, func->name(), emptyString, true); } -void CheckClass::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) +void CheckClassImpl::virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive) { if (inconclusive) { if (mSettings->severity.isEnabled(Severity::warning)) @@ -2055,7 +2294,7 @@ void CheckClass::virtualDestructorError(const Token *tok, const std::string &Bas // warn for "this-x". The indented code may be "this->x" //--------------------------------------------------------------------------- -void CheckClass::thisSubtraction() +void CheckClassImpl::thisSubtraction() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -2075,7 +2314,7 @@ void CheckClass::thisSubtraction() } } -void CheckClass::thisSubtractionError(const Token *tok) +void CheckClassImpl::thisSubtractionError(const Token *tok) { reportError(tok, Severity::warning, "thisSubtraction", "Suspicious pointer subtraction. Did you intend to write '->'?", CWE398, Certainty::normal); } @@ -2084,7 +2323,7 @@ void CheckClass::thisSubtractionError(const Token *tok) // can member function be const? //--------------------------------------------------------------------------- -void CheckClass::checkConst() +void CheckClassImpl::checkConst() { // This is an inconclusive check. False positives: #3322. if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) @@ -2203,7 +2442,7 @@ static const Token* getFuncTokFromThis(const Token* tok) { return Token::Match(tok, "%name% (") ? tok : nullptr; } -bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const +bool CheckClassImpl::isMemberVar(const Scope *scope, const Token *tok) const { bool again = false; @@ -2278,7 +2517,7 @@ bool CheckClass::isMemberVar(const Scope *scope, const Token *tok) const return false; } -bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) +bool CheckClassImpl::isMemberFunc(const Scope *scope, const Token *tok) { if (!tok->function()) { for (const Function &func : scope->functionList) { @@ -2319,7 +2558,7 @@ bool CheckClass::isMemberFunc(const Scope *scope, const Token *tok) return false; } -bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) +bool CheckClassImpl::isConstMemberFunc(const Scope *scope, const Token *tok) { if (!tok->function()) return false; @@ -2346,7 +2585,7 @@ bool CheckClass::isConstMemberFunc(const Scope *scope, const Token *tok) const std::set CheckClass::stl_containers_not_const = { "map", "unordered_map", "std :: map|unordered_map <" }; // start pattern -bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const +bool CheckClassImpl::checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const { if (mTokenizer->hasIfdef(func->functionScope->bodyStart, func->functionScope->bodyEnd)) return false; @@ -2475,7 +2714,7 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, Member } else if (end->strAt(1) == "[") { if (end->varId()) { const Variable *var = end->variable(); - if (var && var->isStlType(stl_containers_not_const)) + if (var && var->isStlType(CheckClass::stl_containers_not_const)) return false; const Token* assignTok = end->next()->astParent(); if (var && assignTok && assignTok->isAssignmentOp() && assignTok->astOperand1() && assignTok->astOperand1()->variable()) { @@ -2590,12 +2829,12 @@ bool CheckClass::checkConstFunc(const Scope *scope, const Function *func, Member return true; } -void CheckClass::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) +void CheckClassImpl::checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic) { checkConstError2(tok, nullptr, classname, funcname, suggestStatic); } -void CheckClass::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) +void CheckClassImpl::checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic) { std::list toks{ tok1 }; if (tok2) @@ -2635,7 +2874,7 @@ namespace { // avoid one-definition-rule violation }; } -void CheckClass::initializerListOrder() +void CheckClassImpl::initializerListOrder() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -2691,7 +2930,7 @@ void CheckClass::initializerListOrder() } } -void CheckClass::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) +void CheckClassImpl::initializerListError(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &varname) { std::list toks = { tok1, tok2 }; reportError(toks, Severity::style, "initializerList", @@ -2709,7 +2948,7 @@ void CheckClass::initializerListError(const Token *tok1, const Token *tok2, cons // Check for self initialization in initialization list //--------------------------------------------------------------------------- -void CheckClass::checkSelfInitialization() +void CheckClassImpl::checkSelfInitialization() { logChecker("CheckClass::checkSelfInitialization"); @@ -2738,7 +2977,7 @@ void CheckClass::checkSelfInitialization() } } -void CheckClass::selfInitializationError(const Token* tok, const std::string& varname) +void CheckClassImpl::selfInitializationError(const Token* tok, const std::string& varname) { reportError(tok, Severity::error, "selfInitialization", "$symbol:" + varname + "\nMember variable '$symbol' is initialized by itself.", CWE665, Certainty::normal); } @@ -2748,7 +2987,7 @@ void CheckClass::selfInitializationError(const Token* tok, const std::string& va // Check for virtual function calls in constructor/destructor //--------------------------------------------------------------------------- -void CheckClass::checkVirtualFunctionCallInConstructor() +void CheckClassImpl::checkVirtualFunctionCallInConstructor() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -2778,7 +3017,7 @@ void CheckClass::checkVirtualFunctionCallInConstructor() } } -const std::list & CheckClass::getVirtualFunctionCalls(const Function & function, +const std::list & CheckClassImpl::getVirtualFunctionCalls(const Function & function, std::map> & virtualFunctionCallsMap) { const std::map>::const_iterator found = virtualFunctionCallsMap.find(&function); @@ -2836,7 +3075,7 @@ const std::list & CheckClass::getVirtualFunctionCalls(const Funct return virtualFunctionCalls; } -void CheckClass::getFirstVirtualFunctionCallStack( +void CheckClassImpl::getFirstVirtualFunctionCallStack( std::map> & virtualFunctionCallsMap, const Token * callToken, std::list & pureFuncStack) @@ -2856,7 +3095,7 @@ void CheckClass::getFirstVirtualFunctionCallStack( getFirstVirtualFunctionCallStack(virtualFunctionCallsMap, firstCall, pureFuncStack); } -void CheckClass::virtualFunctionCallInConstructorError( +void CheckClassImpl::virtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &funcname) @@ -2891,7 +3130,7 @@ void CheckClass::virtualFunctionCallInConstructorError( "Virtual function '" + funcname + "' is called from " + scopeFunctionTypeName + " '" + constructorName + "' at line " + std::to_string(lineNumber) + ". Dynamic binding is not used.", CWE(0U), Certainty::normal); } -void CheckClass::pureVirtualFunctionCallInConstructorError( +void CheckClassImpl::pureVirtualFunctionCallInConstructorError( const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname) @@ -2916,7 +3155,7 @@ void CheckClass::pureVirtualFunctionCallInConstructorError( // Check for members hiding inherited members with the same name //--------------------------------------------------------------------------- -void CheckClass::checkDuplInheritedMembers() +void CheckClassImpl::checkDuplInheritedMembers() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -2999,7 +3238,7 @@ static std::vector getDuplInheritedMemberFunctionsRecursive( return results; } -void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) +void CheckClassImpl::checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase) { const auto resultsVar = getDuplInheritedMembersRecursive(typeCurrent, typeBase); for (const auto& r : resultsVar) { @@ -3018,7 +3257,7 @@ void CheckClass::checkDuplInheritedMembersRecursive(const Type* typeCurrent, con } } -void CheckClass::duplInheritedMembersError(const Token *tok1, const Token* tok2, +void CheckClassImpl::duplInheritedMembersError(const Token *tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction) { @@ -3046,7 +3285,7 @@ enum class CtorType { WITH_BODY }; -void CheckClass::checkCopyCtorAndEqOperator() +void CheckClassImpl::checkCopyCtorAndEqOperator() { // This is disabled because of #8388 // The message must be clarified. How is the behaviour different? @@ -3099,7 +3338,7 @@ void CheckClass::checkCopyCtorAndEqOperator() } } -void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor) +void CheckClassImpl::copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor) { const std::string message = "$symbol:" + classname + "\n" "The " + std::string(isStruct ? "struct" : "class") + " '$symbol' has '" + @@ -3109,7 +3348,7 @@ void CheckClass::copyCtorAndEqOperatorError(const Token *tok, const std::string reportError(tok, Severity::warning, "copyCtorAndEqOperator", message); } -void CheckClass::checkOverride() +void CheckClassImpl::checkOverride() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -3129,7 +3368,7 @@ void CheckClass::checkOverride() } } -void CheckClass::overrideError(const Function *funcInBase, const Function *funcInDerived) +void CheckClassImpl::overrideError(const Function *funcInBase, const Function *funcInDerived) { const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; @@ -3147,7 +3386,7 @@ void CheckClass::overrideError(const Function *funcInBase, const Function *funcI Certainty::normal); } -void CheckClass::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode) +void CheckClassImpl::uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode) { const std::string functionName = funcInDerived ? ((funcInDerived->isDestructor() ? "~" : "") + funcInDerived->name()) : ""; const std::string funcType = (funcInDerived && funcInDerived->isDestructor()) ? "destructor" : "function"; @@ -3209,7 +3448,7 @@ static bool compareTokenRanges(const Token* start1, const Token* end1, const Tok return isEqual; } -void CheckClass::checkUselessOverride() +void CheckClassImpl::checkUselessOverride() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -3268,7 +3507,7 @@ void CheckClass::checkUselessOverride() } } -void CheckClass::checkThisUseAfterFree() +void CheckClassImpl::checkThisUseAfterFree() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -3317,7 +3556,7 @@ void CheckClass::checkThisUseAfterFree() } } -bool CheckClass::checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken) +bool CheckClassImpl::checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken) { if (!func || !func->functionScope) return false; @@ -3356,7 +3595,7 @@ bool CheckClass::checkThisUseAfterFreeRecursive(const Scope *classScope, const F return false; } -void CheckClass::thisUseAfterFree(const Token *self, const Token *free, const Token *use) +void CheckClassImpl::thisUseAfterFree(const Token *self, const Token *free, const Token *use) { std::string selfPointer = self ? self->str() : "ptr"; const ErrorPath errorPath = { ErrorPathItem(self, "Assuming '" + selfPointer + "' is used as 'this'"), ErrorPathItem(free, "Delete '" + selfPointer + "', invalidating 'this'"), ErrorPathItem(use, "Call method when 'this' is invalid") }; @@ -3368,7 +3607,7 @@ void CheckClass::thisUseAfterFree(const Token *self, const Token *free, const To CWE(0), Certainty::normal); } -void CheckClass::checkUnsafeClassRefMember() +void CheckClassImpl::checkUnsafeClassRefMember() { if (!mSettings->safeChecks.classes || !mSettings->severity.isEnabled(Severity::warning)) return; @@ -3392,7 +3631,7 @@ void CheckClass::checkUnsafeClassRefMember() } } -void CheckClass::unsafeClassRefMemberError(const Token *tok, const std::string &varname) +void CheckClassImpl::unsafeClassRefMemberError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "unsafeClassRefMember", "$symbol:" + varname + "\n" @@ -3542,9 +3781,8 @@ bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list all; - CheckClass dummy(nullptr, &settings, &errorLogger); - dummy. - logChecker("CheckClass::analyseWholeProgram"); + CheckClassImpl dummy(nullptr, &settings, &errorLogger); + //dummy.logChecker("CheckClass::analyseWholeProgram"); for (const Check::FileInfo* fi1 : fileInfo) { const MyFileInfo *fi = dynamic_cast(fi1); @@ -3582,4 +3820,194 @@ bool CheckClass::analyseWholeProgram(const CTU::FileInfo *ctu, const std::list(), "f"); + c.virtualFunctionCallInConstructorError(nullptr, std::list(), "f"); + c.overrideError(nullptr, nullptr); + c.thisUseAfterFree(nullptr, nullptr, nullptr); + c.unsafeClassRefMemberError(nullptr, "UnsafeClass::var"); +} + +void CheckClass::checkCopyCtorAndEqOperator(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkCopyCtorAndEqOperator(); +} + +void CheckClass::checkExplicitConstructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkExplicitConstructors(); +} + +void CheckClass::checkDuplInheritedMembers(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkDuplInheritedMembers(); +} + +void CheckClass::copyconstructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.copyconstructors(); +} + +void CheckClass::operatorEqRetRefThis(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.operatorEqRetRefThis(); +} + +void CheckClass::operatorEqToSelf(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.operatorEqToSelf(); +} + +void CheckClass::virtualDestructor(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.virtualDestructor(); +} + +void CheckClass::checkMemset(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkMemset(); +} + +void CheckClass::thisSubtraction(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.thisSubtraction(); +} + +void CheckClass::checkConst(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkConst(); +} + +void CheckClass::initializerListOrder(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.initializerListOrder(); +} + +void CheckClass::initializationListUsage(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.initializationListUsage(); +} + +void CheckClass::checkSelfInitialization(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkSelfInitialization(); +} + +void CheckClass::checkVirtualFunctionCallInConstructor(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkVirtualFunctionCallInConstructor(); +} + +void CheckClass::checkOverride(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkOverride(); +} + +void CheckClass::checkUselessOverride(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkUselessOverride(); +} + +void CheckClass::checkUnsafeClassRefMember(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkUnsafeClassRefMember(); +} + +void CheckClass::checkThisUseAfterFree(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.checkThisUseAfterFree(); +} + +void CheckClass::constructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.constructors(); +} + +void CheckClass::privateFunctions(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckClassImpl c(tokenizer, settings, errorLogger); + c.privateFunctions(); +} diff --git a/lib/checkclass.h b/lib/checkclass.h index bac9f6dd1d5..747e9020d2b 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -23,7 +23,6 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include "symboldatabase.h" #include @@ -34,7 +33,7 @@ class ErrorLogger; class Settings; -class Token; +class Tokenizer; namespace CTU { class FileInfo; @@ -56,112 +55,14 @@ class CPPCHECKLIB CheckClass : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckClass() : Check(myName()) {} + CheckClass() : Check("Class") {} /** @brief Set of the STL types whose operator[] is not const */ static const std::set stl_containers_not_const; private: - /** @brief This constructor is used when running checks. */ - CheckClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger); - /** @brief Run checks on the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (tokenizer.isC()) - return; - - CheckClass checkClass(&tokenizer, tokenizer.getSettings(), errorLogger); - - // can't be a simplified check .. the 'sizeof' is used. - checkClass.checkMemset(); - checkClass.constructors(); - checkClass.privateFunctions(); - checkClass.operatorEqRetRefThis(); - checkClass.thisSubtraction(); - checkClass.operatorEqToSelf(); - checkClass.initializerListOrder(); - checkClass.initializationListUsage(); - checkClass.checkSelfInitialization(); - checkClass.virtualDestructor(); - checkClass.checkConst(); - checkClass.copyconstructors(); - checkClass.checkVirtualFunctionCallInConstructor(); - checkClass.checkDuplInheritedMembers(); - checkClass.checkExplicitConstructors(); - checkClass.checkCopyCtorAndEqOperator(); - checkClass.checkOverride(); - checkClass.checkUselessOverride(); - checkClass.checkThisUseAfterFree(); - checkClass.checkUnsafeClassRefMember(); - } - - /** @brief %Check that all class constructors are ok */ - void constructors(); - - /** @brief %Check that constructors with single parameter are explicit, - * if they has to be.*/ - void checkExplicitConstructors(); - - /** @brief %Check that all private functions are called */ - void privateFunctions(); - - /** - * @brief %Check that the memsets are valid. - * The 'memset' function can do dangerous things if used wrong. If it - * is used on STL containers for instance it will clear all its data - * and then the STL container may leak memory or worse have an invalid state. - * It can also overwrite the virtual table. - * Important: The checking doesn't work on simplified tokens list. - */ - void checkMemset(); - void checkMemsetType(const Scope *start, const Token *tok, const Scope *type, bool allocation, std::set parsedTypes); - - /** @brief 'operator=' should return reference to *this */ - void operatorEqRetRefThis(); // Warning upon no "return *this;" - - /** @brief 'operator=' should check for assignment to self */ - void operatorEqToSelf(); // Warning upon no check for assignment to self - - /** @brief The destructor in a base class should be virtual */ - void virtualDestructor(); - - /** @brief warn for "this-x". The indented code may be "this->x" */ - void thisSubtraction(); - - /** @brief can member function be const? */ - void checkConst(); - - /** @brief Check initializer list order */ - void initializerListOrder(); - - /** @brief Suggest using initialization list */ - void initializationListUsage(); - - /** @brief Check for initialization of a member with itself */ - void checkSelfInitialization(); - - void copyconstructors(); - - /** @brief call of virtual function in constructor/destructor */ - void checkVirtualFunctionCallInConstructor(); - - /** @brief Check duplicated inherited members */ - void checkDuplInheritedMembers(); - - /** @brief Check that copy constructor and operator defined together */ - void checkCopyCtorAndEqOperator(); - - /** @brief Check that the override keyword is used when overriding virtual functions */ - void checkOverride(); - - /** @brief Check that the overriden function is not identical to the base function */ - void checkUselessOverride(); - - /** @brief When "self pointer" is destroyed, 'this' might become invalid. */ - void checkThisUseAfterFree(); - - /** @brief Unsafe class check - const reference member */ - void checkUnsafeClassRefMember(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override ; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const override; @@ -171,91 +72,7 @@ class CPPCHECKLIB CheckClass : public Check { /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; - const SymbolDatabase* mSymbolDatabase{}; - - // Reporting errors.. - void noConstructorError(const Token *tok, const std::string &classname, bool isStruct); - void noExplicitConstructorError(const Token *tok, const std::string &classname, bool isStruct); - //void copyConstructorMallocError(const Token *cctor, const Token *alloc, const std::string& var_name); - void copyConstructorShallowCopyError(const Token *tok, const std::string& varname); - void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); - void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); - void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); - void uninitVarError(const Token *tok, bool isprivate, Function::Type functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive); - void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname); - void missingMemberCopyError(const Token *tok, Function::Type functionType, const std::string& classname, const std::string& varname); - void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive); - void unusedPrivateFunctionError(const Token *tok, const std::string &classname, const std::string &funcname); - void memsetError(const Token *tok, const std::string &memfunc, const std::string &classname, const std::string &type, bool isContainer = false); - void memsetErrorReference(const Token *tok, const std::string &memfunc, const std::string &type); - void memsetErrorFloat(const Token *tok, const std::string &type); - void mallocOnClassError(const Token* tok, const std::string &memfunc, const Token* classTok, const std::string &classname); - void mallocOnClassWarning(const Token* tok, const std::string &memfunc, const Token* classTok); - void virtualDestructorError(const Token *tok, const std::string &Base, const std::string &Derived, bool inconclusive); - void thisSubtractionError(const Token *tok); - void operatorEqRetRefThisError(const Token *tok); - void operatorEqShouldBeLeftUnimplementedError(const Token *tok); - void operatorEqMissingReturnStatementError(const Token *tok, bool error); - void operatorEqToSelfError(const Token *tok); - void checkConstError(const Token *tok, const std::string &classname, const std::string &funcname, bool suggestStatic); - void checkConstError2(const Token *tok1, const Token *tok2, const std::string &classname, const std::string &funcname, bool suggestStatic); - void initializerListError(const Token *tok1,const Token *tok2, const std::string & classname, const std::string &varname); - void suggestInitializationList(const Token *tok, const std::string& varname); - void selfInitializationError(const Token* tok, const std::string& varname); - void pureVirtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &purefuncname); - void virtualFunctionCallInConstructorError(const Function * scopeFunction, const std::list & tokStack, const std::string &funcname); - void duplInheritedMembersError(const Token* tok1, const Token* tok2, const std::string &derivedName, const std::string &baseName, const std::string &memberName, bool derivedIsStruct, bool baseIsStruct, bool isFunction = false); - void copyCtorAndEqOperatorError(const Token *tok, const std::string &classname, bool isStruct, bool hasCopyCtor); - void overrideError(const Function *funcInBase, const Function *funcInDerived); - void uselessOverrideError(const Function *funcInBase, const Function *funcInDerived, bool isSameCode = false); - void thisUseAfterFree(const Token *self, const Token *free, const Token *use); - void unsafeClassRefMemberError(const Token *tok, const std::string &varname); - void checkDuplInheritedMembersRecursive(const Type* typeCurrent, const Type* typeBase); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckClass c(nullptr, settings, errorLogger); - c.noConstructorError(nullptr, "classname", false); - c.noExplicitConstructorError(nullptr, "classname", false); - //c.copyConstructorMallocError(nullptr, 0, "var"); - c.copyConstructorShallowCopyError(nullptr, "var"); - c.noCopyConstructorError(nullptr, false, nullptr, false); - c.noOperatorEqError(nullptr, false, nullptr, false); - c.noDestructorError(nullptr, false, nullptr); - c.uninitVarError(nullptr, false, Function::eConstructor, "classname", "varname", false, false); - c.uninitVarError(nullptr, true, Function::eConstructor, "classname", "varnamepriv", false, false); - c.uninitVarError(nullptr, false, Function::eConstructor, "classname", "varname", true, false); - c.uninitVarError(nullptr, true, Function::eConstructor, "classname", "varnamepriv", true, false); - c.missingMemberCopyError(nullptr, Function::eConstructor, "classname", "varnamepriv"); - c.operatorEqVarError(nullptr, "classname", emptyString, false); - c.unusedPrivateFunctionError(nullptr, "classname", "funcname"); - c.memsetError(nullptr, "memfunc", "classname", "class"); - c.memsetErrorReference(nullptr, "memfunc", "class"); - c.memsetErrorFloat(nullptr, "class"); - c.mallocOnClassWarning(nullptr, "malloc", nullptr); - c.mallocOnClassError(nullptr, "malloc", nullptr, "std::string"); - c.virtualDestructorError(nullptr, "Base", "Derived", false); - c.thisSubtractionError(nullptr); - c.operatorEqRetRefThisError(nullptr); - c.operatorEqMissingReturnStatementError(nullptr, true); - c.operatorEqShouldBeLeftUnimplementedError(nullptr); - c.operatorEqToSelfError(nullptr); - c.checkConstError(nullptr, "class", "function", false); - c.checkConstError(nullptr, "class", "function", true); - c.initializerListError(nullptr, nullptr, "class", "variable"); - c.suggestInitializationList(nullptr, "variable"); - c.selfInitializationError(nullptr, "var"); - c.duplInheritedMembersError(nullptr, nullptr, "class", "class", "variable", false, false); - c.copyCtorAndEqOperatorError(nullptr, "class", false, false); - c.pureVirtualFunctionCallInConstructorError(nullptr, std::list(), "f"); - c.virtualFunctionCallInConstructorError(nullptr, std::list(), "f"); - c.overrideError(nullptr, nullptr); - c.thisUseAfterFree(nullptr, nullptr, nullptr); - c.unsafeClassRefMemberError(nullptr, "UnsafeClass::var"); - } - - static std::string myName() { - return "Class"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check the code for each class.\n" @@ -283,128 +100,27 @@ class CPPCHECKLIB CheckClass : public Check { "- Check that the 'one definition rule' is not violated\n"; } - // operatorEqRetRefThis helper functions - void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last); - void checkReturnPtrThis(const Scope *scope, const Function *func, const Token *tok, const Token *last, std::set& analyzedFunctions); - - // operatorEqToSelf helper functions - bool hasAllocation(const Function *func, const Scope* scope) const; - bool hasAllocation(const Function *func, const Scope* scope, const Token *start, const Token *end) const; - bool hasAllocationInIfScope(const Function *func, const Scope* scope, const Token *ifStatementScopeStart) const; - static bool hasAssignSelf(const Function *func, const Token *rhs, const Token **out_ifStatementScopeStart); - enum class Bool { TRUE, FALSE, BAILOUT }; - static Bool isInverted(const Token *tok, const Token *rhs); - static const Token * getIfStmtBodyStart(const Token *tok, const Token *rhs); - - // checkConst helper functions - bool isMemberVar(const Scope *scope, const Token *tok) const; - static bool isMemberFunc(const Scope *scope, const Token *tok); - static bool isConstMemberFunc(const Scope *scope, const Token *tok); - enum class MemberAccess { NONE, SELF, MEMBER }; - bool checkConstFunc(const Scope *scope, const Function *func, MemberAccess& memberAccessed) const; - - // constructors helper function - /** @brief Information about a member variable. Used when checking for uninitialized variables */ - struct Usage { - explicit Usage(const Variable *var) : var(var) {} - - /** Variable that this usage is for */ - const Variable *var; - - /** @brief has this variable been assigned? */ - bool assign{}; - - /** @brief has this variable been initialized? */ - bool init{}; - }; - - static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope); - - /** - * @brief Create usage list that contains all scope members and also members - * of base classes without constructors. - * @param scope current class scope - */ - static std::vector createUsageList(const Scope *scope); - - /** - * @brief assign a variable in the varlist - * @param usageList reference to usage vector - * @param varid id of variable to mark assigned - */ - static void assignVar(std::vector &usageList, nonneg int varid); - - /** - * @brief assign a variable in the varlist - * @param usageList reference to usage vector - * @param vartok variable token - */ - static void assignVar(std::vector &usageList, const Token *vartok); - - /** - * @brief initialize a variable in the varlist - * @param usageList reference to usage vector - * @param varid id of variable to mark initialized - */ - static void initVar(std::vector &usageList, nonneg int varid); - - /** - * @brief set all variables in list assigned - * @param usageList reference to usage vector - */ - static void assignAllVar(std::vector &usageList); - - /** - * @brief set all variable in list assigned, if visible from given scope - * @param usageList reference to usage vector - * @param scope scope from which usages must be visible - */ - static void assignAllVarsVisibleFromScope(std::vector &usageList, const Scope *scope); - - /** - * @brief set all variables in list not assigned and not initialized - * @param usageList reference to usage vector - */ - static void clearAllVar(std::vector &usageList); - - /** - * @brief parse a scope for a constructor or member function and set the "init" flags in the provided varlist - * @param func reference to the function that should be checked - * @param callstack the function doesn't look into recursive function calls. - * @param scope pointer to variable Scope - * @param usage reference to usage vector - */ - void initializeVarList(const Function &func, std::list &callstack, const Scope *scope, std::vector &usage) const; - - /** - * @brief gives a list of tokens where virtual functions are called directly or indirectly - * @param function function to be checked - * @param virtualFunctionCallsMap map of results for already checked functions - * @return list of tokens where pure virtual functions are called - */ - const std::list & getVirtualFunctionCalls( - const Function & function, - std::map> & virtualFunctionCallsMap); - - /** - * @brief looks for the first virtual function call stack - * @param virtualFunctionCallsMap map of results obtained from getVirtualFunctionCalls - * @param callToken token where pure virtual function is called directly or indirectly - * @param[in,out] pureFuncStack list to append the stack - */ - static void getFirstVirtualFunctionCallStack( - std::map> & virtualFunctionCallsMap, - const Token *callToken, - std::list & pureFuncStack); - - static bool canNotCopy(const Scope *scope); - - static bool canNotMove(const Scope *scope); - - /** - * @brief Helper for checkThisUseAfterFree - */ - bool checkThisUseAfterFreeRecursive(const Scope *classScope, const Function *func, const Variable *selfPointer, std::set callstack, const Token **freeToken); + // for testing + static void checkCopyCtorAndEqOperator(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkExplicitConstructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkDuplInheritedMembers(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void copyconstructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void operatorEqRetRefThis(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void operatorEqToSelf(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void virtualDestructor(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkMemset(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void thisSubtraction(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkConst(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void initializerListOrder(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void initializationListUsage(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkSelfInitialization(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkVirtualFunctionCallInConstructor(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkOverride(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkUselessOverride(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkUnsafeClassRefMember(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkThisUseAfterFree(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void constructors(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void privateFunctions(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index 8e5e3f134ba..960c62527d8 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -23,6 +23,7 @@ #include "checkcondition.h" #include "astutils.h" +#include "checkimpl.h" #include "library.h" #include "platform.h" #include "settings.h" @@ -53,9 +54,113 @@ static const CWE CWE571(571U); // Expression is Always True // Register this check class (by creating a static instance of it) namespace { CheckCondition instance; + + + class CheckConditionImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckConditionImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** mismatching assignment / comparison */ + void assignIf(); + + /** parse scopes recursively */ + bool assignIfParseScope(const Token * const assignTok, + const Token * const startTok, + const nonneg int varid, + const bool islocal, + const char bitop, + const MathLib::bigint num); + + /** check bitmask using | instead of & */ + void checkBadBitmaskCheck(); + + /** mismatching lhs and rhs in comparison */ + void comparison(); + + void duplicateCondition(); + + /** match 'if' and 'else if' conditions */ + void multiCondition(); + + /** + * multiconditions #2 + * - Opposite inner conditions => always false + * - (TODO) Same/Overlapping inner condition => always true + * - same condition after early exit => always false + **/ + void multiCondition2(); + + /** @brief %Check for testing for mutual exclusion over ||*/ + void checkIncorrectLogicOperator(); + + /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ + void checkModuloAlwaysTrueFalse(); + + /** @brief Suspicious condition (assignment+comparison) */ + void clarifyCondition(); + + /** @brief Condition is always true/false */ + void alwaysTrueFalse(); + + /** @brief %Check for invalid test for overflow 'x+100 < x' */ + void checkInvalidTestForOverflow(); + + /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */ + void checkPointerAdditionResultNotNull(); + + void checkDuplicateConditionalAssign(); + + /** @brief Assignment in condition */ + void checkAssignmentInCondition(); + + // The conditions that have been diagnosed + std::set mCondDiags; + bool diag(const Token* tok, bool insert=true); + bool isAliased(const std::set &vars) const; + bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; + void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); + void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); + void badBitmaskCheckError(const Token *tok, bool isNoOp = false); + void comparisonError(const Token *tok, + const std::string &bitop, + MathLib::bigint value1, + const std::string &op, + MathLib::bigint value2, + bool result); + void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath); + void overlappingElseIfConditionError(const Token *tok, nonneg int line1); + void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath); + + void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); + + void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); + + void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath); + + void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors); + void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive); + + void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); + + void clarifyConditionError(const Token *tok, bool assign, bool boolop); + + void alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value); + + void invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace); + void pointerAdditionResultNotNullError(const Token *tok, const Token *calc); + + void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant = false); + + void assignmentInCondition(const Token *eq); + + void checkCompareValueOutOfTypeRange(); + void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result); + }; } -bool CheckCondition::diag(const Token* tok, bool insert) +bool CheckConditionImpl::diag(const Token* tok, bool insert) { if (!tok) return false; @@ -76,7 +181,7 @@ bool CheckCondition::diag(const Token* tok, bool insert) return true; } -bool CheckCondition::isAliased(const std::set &vars) const +bool CheckConditionImpl::isAliased(const std::set &vars) const { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "= & %var% ;") && vars.find(tok->tokAt(2)->varId()) != vars.end()) @@ -85,7 +190,7 @@ bool CheckCondition::isAliased(const std::set &vars) const return false; } -void CheckCondition::assignIf() +void CheckConditionImpl::assignIf() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -158,7 +263,7 @@ static bool isParameterChanged(const Token *partok) } /** parse scopes recursively */ -bool CheckCondition::assignIfParseScope(const Token * const assignTok, +bool CheckConditionImpl::assignIfParseScope(const Token * const assignTok, const Token * const startTok, const nonneg int varid, const bool islocal, @@ -236,7 +341,7 @@ bool CheckCondition::assignIfParseScope(const Token * const assignTok, return false; } -void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result) +void CheckConditionImpl::assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result) { if (tok2 && diag(tok2->tokAt(2))) return; @@ -248,7 +353,7 @@ void CheckCondition::assignIfError(const Token *tok1, const Token *tok2, const s } -void CheckCondition::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2) +void CheckConditionImpl::mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2) { std::list locations = { tok1, tok2 }; @@ -304,7 +409,7 @@ static bool isOperandExpanded(const Token *tok) return false; } -void CheckCondition::checkBadBitmaskCheck() +void CheckConditionImpl::checkBadBitmaskCheck() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -344,7 +449,7 @@ void CheckCondition::checkBadBitmaskCheck() } } -void CheckCondition::badBitmaskCheckError(const Token *tok, bool isNoOp) +void CheckConditionImpl::badBitmaskCheckError(const Token *tok, bool isNoOp) { if (isNoOp) reportError(tok, Severity::style, "badBitmaskCheck", "Operator '|' with one operand equal to zero is redundant.", CWE571, Certainty::normal); @@ -352,7 +457,7 @@ void CheckCondition::badBitmaskCheckError(const Token *tok, bool isNoOp) reportError(tok, Severity::warning, "badBitmaskCheck", "Result of operator '|' is always true if one operand is non-zero. Did you intend to use '&'?", CWE571, Certainty::normal); } -void CheckCondition::comparison() +void CheckConditionImpl::comparison() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -415,7 +520,7 @@ void CheckCondition::comparison() } } -void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result) +void CheckConditionImpl::comparisonError(const Token *tok, const std::string &bitop, MathLib::bigint value1, const std::string &op, MathLib::bigint value2, bool result) { if (tok && (diag(tok) | diag(tok->astParent()))) return; @@ -431,7 +536,7 @@ void CheckCondition::comparisonError(const Token *tok, const std::string &bitop, reportError(tok, Severity::style, "comparisonError", errmsg, CWE398, Certainty::normal); } -bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const +bool CheckConditionImpl::isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const { if (!cond1 || !cond2) return false; @@ -472,7 +577,7 @@ bool CheckCondition::isOverlappingCond(const Token * const cond1, const Token * return false; } -void CheckCondition::duplicateCondition() +void CheckConditionImpl::duplicateCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -511,7 +616,7 @@ void CheckCondition::duplicateCondition() } } -void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath) +void CheckConditionImpl::duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; @@ -523,7 +628,7 @@ void CheckCondition::duplicateConditionError(const Token *tok1, const Token *tok reportError(errorPath, Severity::style, "duplicateCondition", msg, CWE398, Certainty::normal); } -void CheckCondition::multiCondition() +void CheckConditionImpl::multiCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -566,7 +671,7 @@ void CheckCondition::multiCondition() } } -void CheckCondition::overlappingElseIfConditionError(const Token *tok, nonneg int line1) +void CheckConditionImpl::overlappingElseIfConditionError(const Token *tok, nonneg int line1) { if (diag(tok)) return; @@ -577,7 +682,7 @@ void CheckCondition::overlappingElseIfConditionError(const Token *tok, nonneg in reportError(tok, Severity::style, "multiCondition", errmsg.str(), CWE398, Certainty::normal); } -void CheckCondition::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath) +void CheckConditionImpl::oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath) { if (diag(ifCond) & diag(elseIfCond)) return; @@ -613,7 +718,7 @@ static bool isNonConstFunctionCall(const Token *ftok, const Library &library) return true; } -void CheckCondition::multiCondition2() +void CheckConditionImpl::multiCondition2() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -841,7 +946,7 @@ static std::string innerSmtString(const Token * tok) return top->str(); } -void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) +void CheckConditionImpl::oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; @@ -856,7 +961,7 @@ void CheckCondition::oppositeInnerConditionError(const Token *tok1, const Token* reportError(errorPath, Severity::warning, "oppositeInnerCondition", msg, CWE398, Certainty::normal); } -void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) +void CheckConditionImpl::identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath) { if (diag(tok1) & diag(tok2)) return; @@ -871,7 +976,7 @@ void CheckCondition::identicalInnerConditionError(const Token *tok1, const Token reportError(errorPath, Severity::warning, "identicalInnerCondition", msg, CWE398, Certainty::normal); } -void CheckCondition::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) +void CheckConditionImpl::identicalConditionAfterEarlyExitError(const Token *cond1, const Token* cond2, ErrorPath errorPath) { if (diag(cond1) & diag(cond2)) return; @@ -1121,7 +1226,7 @@ static bool isIfConstexpr(const Token* tok) { return top && Token::simpleMatch(top->astOperand1(), "if") && top->astOperand1()->isConstexpr(); } -void CheckCondition::checkIncorrectLogicOperator() +void CheckConditionImpl::checkIncorrectLogicOperator() { const bool printStyle = mSettings->severity.isEnabled(Severity::style); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); @@ -1332,7 +1437,7 @@ void CheckCondition::checkIncorrectLogicOperator() } } -void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors) +void CheckConditionImpl::incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors) { if (diag(tok)) return; @@ -1349,7 +1454,7 @@ void CheckCondition::incorrectLogicOperatorError(const Token *tok, const std::st "Are these conditions necessary? Did you intend to use || instead? Are the numbers correct? Are you comparing the correct variables?", CWE570, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckCondition::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive) +void CheckConditionImpl::redundantConditionError(const Token *tok, const std::string &text, bool inconclusive) { if (diag(tok)) return; @@ -1359,7 +1464,7 @@ void CheckCondition::redundantConditionError(const Token *tok, const std::string //----------------------------------------------------------------------------- // Detect "(var % val1) > val2" where val2 is >= val1. //----------------------------------------------------------------------------- -void CheckCondition::checkModuloAlwaysTrueFalse() +void CheckConditionImpl::checkModuloAlwaysTrueFalse() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -1389,7 +1494,7 @@ void CheckCondition::checkModuloAlwaysTrueFalse() } } -void CheckCondition::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) +void CheckConditionImpl::moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal) { if (diag(tok)) return; @@ -1415,7 +1520,7 @@ static int countPar(const Token *tok1, const Token *tok2) // Clarify condition '(x = a < 0)' into '((x = a) < 0)' or '(x = (a < 0))' // Clarify condition '(a & b == c)' into '((a & b) == c)' or '(a & (b == c))' //--------------------------------------------------------------------------- -void CheckCondition::clarifyCondition() +void CheckConditionImpl::clarifyCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1458,7 +1563,7 @@ void CheckCondition::clarifyCondition() } } -void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool boolop) +void CheckConditionImpl::clarifyConditionError(const Token *tok, bool assign, bool boolop) { std::string errmsg; @@ -1481,7 +1586,7 @@ void CheckCondition::clarifyConditionError(const Token *tok, bool assign, bool b errmsg, CWE398, Certainty::normal); } -void CheckCondition::alwaysTrueFalse() +void CheckConditionImpl::alwaysTrueFalse() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1618,7 +1723,7 @@ void CheckCondition::alwaysTrueFalse() } } -void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value) +void CheckConditionImpl::alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value) { const bool alwaysTrue = value && (value->intvalue != 0 || value->isImpossible()); const std::string expr = tok ? tok->expressionString() : std::string("x"); @@ -1632,7 +1737,7 @@ void CheckCondition::alwaysTrueFalseError(const Token* tok, const Token* conditi (alwaysTrue ? CWE571 : CWE570), Certainty::normal); } -void CheckCondition::checkInvalidTestForOverflow() +void CheckConditionImpl::checkInvalidTestForOverflow() { // Interesting blogs: // https://www.airs.com/blog/archives/120 @@ -1721,7 +1826,7 @@ void CheckCondition::checkInvalidTestForOverflow() } } -void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace) +void CheckConditionImpl::invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace) { const std::string expr = (tok ? tok->expressionString() : std::string("x + c < x")); const std::string overflow = (valueType && valueType->pointer) ? "pointer overflow" : "signed integer overflow"; @@ -1736,7 +1841,7 @@ void CheckCondition::invalidTestForOverflow(const Token* tok, const ValueType *v } -void CheckCondition::checkPointerAdditionResultNotNull() +void CheckConditionImpl::checkPointerAdditionResultNotNull() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -1777,13 +1882,13 @@ void CheckCondition::checkPointerAdditionResultNotNull() } } -void CheckCondition::pointerAdditionResultNotNullError(const Token *tok, const Token *calc) +void CheckConditionImpl::pointerAdditionResultNotNullError(const Token *tok, const Token *calc) { const std::string s = calc ? calc->expressionString() : "ptr+1"; reportError(tok, Severity::warning, "pointerAdditionResultNotNull", "Comparison is wrong. Result of '" + s + "' can't be 0 unless there is pointer overflow, and pointer overflow is undefined behaviour."); } -void CheckCondition::checkDuplicateConditionalAssign() +void CheckConditionImpl::checkDuplicateConditionalAssign() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1840,7 +1945,7 @@ void CheckCondition::checkDuplicateConditionalAssign() } } -void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant) +void CheckConditionImpl::duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant) { ErrorPath errors; std::string msg = "Duplicate expression for the condition and assignment."; @@ -1862,7 +1967,7 @@ void CheckCondition::duplicateConditionalAssignError(const Token *condTok, const } -void CheckCondition::checkAssignmentInCondition() +void CheckConditionImpl::checkAssignmentInCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1896,7 +2001,7 @@ void CheckCondition::checkAssignmentInCondition() } } -void CheckCondition::assignmentInCondition(const Token *eq) +void CheckConditionImpl::assignmentInCondition(const Token *eq) { std::string expr = eq ? eq->expressionString() : "x=y"; @@ -1909,7 +2014,7 @@ void CheckCondition::assignmentInCondition(const Token *eq) Certainty::normal); } -void CheckCondition::checkCompareValueOutOfTypeRange() +void CheckConditionImpl::checkCompareValueOutOfTypeRange() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -2028,7 +2133,7 @@ void CheckCondition::checkCompareValueOutOfTypeRange() } } -void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result) +void CheckConditionImpl::compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result) { reportError( comparison, @@ -2038,3 +2143,48 @@ void CheckCondition::compareValueOutOfTypeRangeError(const Token *comparison, co CWE398, Certainty::normal); } + +void CheckCondition::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckConditionImpl checkCondition(&tokenizer, tokenizer.getSettings(), errorLogger); + checkCondition.multiCondition(); + checkCondition.clarifyCondition(); // not simplified because ifAssign + checkCondition.multiCondition2(); + checkCondition.checkIncorrectLogicOperator(); + checkCondition.checkInvalidTestForOverflow(); + checkCondition.duplicateCondition(); + checkCondition.checkPointerAdditionResultNotNull(); + checkCondition.checkDuplicateConditionalAssign(); + checkCondition.assignIf(); + checkCondition.checkBadBitmaskCheck(); + checkCondition.comparison(); + checkCondition.checkModuloAlwaysTrueFalse(); + checkCondition.checkAssignmentInCondition(); + checkCondition.checkCompareValueOutOfTypeRange(); + checkCondition.alwaysTrueFalse(); +} + +void CheckCondition::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckConditionImpl c(nullptr, settings, errorLogger); + + ErrorPath errorPath; + + c.assignIfError(nullptr, nullptr, emptyString, false); + c.badBitmaskCheckError(nullptr); + c.comparisonError(nullptr, "&", 6, "==", 1, false); + c.duplicateConditionError(nullptr, nullptr, errorPath); + c.overlappingElseIfConditionError(nullptr, 1); + c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); + c.oppositeInnerConditionError(nullptr, nullptr, errorPath); + c.identicalInnerConditionError(nullptr, nullptr, errorPath); + c.identicalConditionAfterEarlyExitError(nullptr, nullptr, errorPath); + c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, errorPath); + c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false); + c.moduloAlwaysTrueFalseError(nullptr, "1"); + c.clarifyConditionError(nullptr, true, false); + c.alwaysTrueFalseError(nullptr, nullptr, nullptr); + c.invalidTestForOverflow(nullptr, nullptr, "false"); + c.pointerAdditionResultNotNullError(nullptr, nullptr); + c.duplicateConditionalAssignError(nullptr, nullptr); + c.assignmentInCondition(nullptr); + c.compareValueOutOfTypeRangeError(nullptr, "unsigned char", 256, true); +} diff --git a/lib/checkcondition.h b/lib/checkcondition.h index d0aba1b2d99..ed602264729 100644 --- a/lib/checkcondition.h +++ b/lib/checkcondition.h @@ -24,21 +24,13 @@ #include "check.h" #include "config.h" -#include "mathlib.h" #include "errortypes.h" -#include "tokenize.h" -#include #include class Settings; -class Token; class ErrorLogger; -class ValueType; - -namespace ValueFlow { - class Value; -} +class Tokenizer; /// @addtogroup Checks /// @{ @@ -50,157 +42,12 @@ namespace ValueFlow { class CPPCHECKLIB CheckCondition : public Check { public: /** This constructor is used when registering the CheckAssignIf */ - CheckCondition() : Check(myName()) {} + CheckCondition() : Check("Condition") {} private: - /** This constructor is used when running checks. */ - CheckCondition(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckCondition checkCondition(&tokenizer, tokenizer.getSettings(), errorLogger); - checkCondition.multiCondition(); - checkCondition.clarifyCondition(); // not simplified because ifAssign - checkCondition.multiCondition2(); - checkCondition.checkIncorrectLogicOperator(); - checkCondition.checkInvalidTestForOverflow(); - checkCondition.duplicateCondition(); - checkCondition.checkPointerAdditionResultNotNull(); - checkCondition.checkDuplicateConditionalAssign(); - checkCondition.assignIf(); - checkCondition.checkBadBitmaskCheck(); - checkCondition.comparison(); - checkCondition.checkModuloAlwaysTrueFalse(); - checkCondition.checkAssignmentInCondition(); - checkCondition.checkCompareValueOutOfTypeRange(); - checkCondition.alwaysTrueFalse(); - } - - /** mismatching assignment / comparison */ - void assignIf(); - - /** parse scopes recursively */ - bool assignIfParseScope(const Token * const assignTok, - const Token * const startTok, - const nonneg int varid, - const bool islocal, - const char bitop, - const MathLib::bigint num); - - /** check bitmask using | instead of & */ - void checkBadBitmaskCheck(); - - /** mismatching lhs and rhs in comparison */ - void comparison(); - - void duplicateCondition(); - - /** match 'if' and 'else if' conditions */ - void multiCondition(); - - /** - * multiconditions #2 - * - Opposite inner conditions => always false - * - (TODO) Same/Overlapping inner condition => always true - * - same condition after early exit => always false - **/ - void multiCondition2(); - - /** @brief %Check for testing for mutual exclusion over ||*/ - void checkIncorrectLogicOperator(); - - /** @brief %Check for suspicious usage of modulo (e.g. "if(var % 4 == 4)") */ - void checkModuloAlwaysTrueFalse(); - - /** @brief Suspicious condition (assignment+comparison) */ - void clarifyCondition(); - - /** @brief Condition is always true/false */ - void alwaysTrueFalse(); - - /** @brief %Check for invalid test for overflow 'x+100 < x' */ - void checkInvalidTestForOverflow(); - - /** @brief Check if pointer addition result is NULL '(ptr + 1) == NULL' */ - void checkPointerAdditionResultNotNull(); - - void checkDuplicateConditionalAssign(); - - /** @brief Assignment in condition */ - void checkAssignmentInCondition(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - // The conditions that have been diagnosed - std::set mCondDiags; - bool diag(const Token* tok, bool insert=true); - bool isAliased(const std::set &vars) const; - bool isOverlappingCond(const Token * const cond1, const Token * const cond2, bool pure) const; - void assignIfError(const Token *tok1, const Token *tok2, const std::string &condition, bool result); - void mismatchingBitAndError(const Token *tok1, const MathLib::bigint num1, const Token *tok2, const MathLib::bigint num2); - void badBitmaskCheckError(const Token *tok, bool isNoOp = false); - void comparisonError(const Token *tok, - const std::string &bitop, - MathLib::bigint value1, - const std::string &op, - MathLib::bigint value2, - bool result); - void duplicateConditionError(const Token *tok1, const Token *tok2, ErrorPath errorPath); - void overlappingElseIfConditionError(const Token *tok, nonneg int line1); - void oppositeElseIfConditionError(const Token *ifCond, const Token *elseIfCond, ErrorPath errorPath); - - void oppositeInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); - - void identicalInnerConditionError(const Token *tok1, const Token* tok2, ErrorPath errorPath); - - void identicalConditionAfterEarlyExitError(const Token *cond1, const Token *cond2, ErrorPath errorPath); - - void incorrectLogicOperatorError(const Token *tok, const std::string &condition, bool always, bool inconclusive, ErrorPath errors); - void redundantConditionError(const Token *tok, const std::string &text, bool inconclusive); - - void moduloAlwaysTrueFalseError(const Token* tok, const std::string& maxVal); - - void clarifyConditionError(const Token *tok, bool assign, bool boolop); - - void alwaysTrueFalseError(const Token* tok, const Token* condition, const ValueFlow::Value* value); - - void invalidTestForOverflow(const Token* tok, const ValueType *valueType, const std::string &replace); - void pointerAdditionResultNotNullError(const Token *tok, const Token *calc); - - void duplicateConditionalAssignError(const Token *condTok, const Token* assignTok, bool isRedundant = false); - - void assignmentInCondition(const Token *eq); - - void checkCompareValueOutOfTypeRange(); - void compareValueOutOfTypeRangeError(const Token *comparison, const std::string &type, long long value, bool result); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckCondition c(nullptr, settings, errorLogger); - - ErrorPath errorPath; - - c.assignIfError(nullptr, nullptr, emptyString, false); - c.badBitmaskCheckError(nullptr); - c.comparisonError(nullptr, "&", 6, "==", 1, false); - c.duplicateConditionError(nullptr, nullptr, errorPath); - c.overlappingElseIfConditionError(nullptr, 1); - c.mismatchingBitAndError(nullptr, 0xf0, nullptr, 1); - c.oppositeInnerConditionError(nullptr, nullptr, errorPath); - c.identicalInnerConditionError(nullptr, nullptr, errorPath); - c.identicalConditionAfterEarlyExitError(nullptr, nullptr, errorPath); - c.incorrectLogicOperatorError(nullptr, "foo > 3 && foo < 4", true, false, errorPath); - c.redundantConditionError(nullptr, "If x > 11 the condition x > 10 is always true.", false); - c.moduloAlwaysTrueFalseError(nullptr, "1"); - c.clarifyConditionError(nullptr, true, false); - c.alwaysTrueFalseError(nullptr, nullptr, nullptr); - c.invalidTestForOverflow(nullptr, nullptr, "false"); - c.pointerAdditionResultNotNullError(nullptr, nullptr); - c.duplicateConditionalAssignError(nullptr, nullptr); - c.assignmentInCondition(nullptr); - c.compareValueOutOfTypeRangeError(nullptr, "unsigned char", 256, true); - } - - static std::string myName() { - return "Condition"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Match conditions with assignments and other conditions:\n" diff --git a/lib/checkexceptionsafety.cpp b/lib/checkexceptionsafety.cpp index 2c404e8312e..5097903338e 100644 --- a/lib/checkexceptionsafety.cpp +++ b/lib/checkexceptionsafety.cpp @@ -19,11 +19,13 @@ //--------------------------------------------------------------------------- #include "checkexceptionsafety.h" +#include "checkimpl.h" #include "errortypes.h" #include "library.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" +#include "tokenize.h" #include #include @@ -32,18 +34,57 @@ //--------------------------------------------------------------------------- -// Register CheckExceptionSafety.. -namespace { - CheckExceptionSafety instance; -} - static const CWE CWE398(398U); // Indicator of Poor Code Quality static const CWE CWE703(703U); // Improper Check or Handling of Exceptional Conditions static const CWE CWE480(480U); // Use of Incorrect Operator //--------------------------------------------------------------------------- -void CheckExceptionSafety::destructors() +// Register CheckExceptionSafety.. +namespace { + CheckExceptionSafety instance; + + class CheckExceptionSafetyImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckExceptionSafetyImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** Don't throw exceptions in destructors */ + void destructors(); + + /** deallocating memory and then throw (dead pointer) */ + void deallocThrow(); + + /** Don't rethrow a copy of the caught exception; use a bare throw instead */ + void checkRethrowCopy(); + + /** @brief %Check for exceptions that are caught by value instead of by reference */ + void checkCatchExceptionByValue(); + + /** @brief %Check for functions that throw that shouldn't */ + void nothrowThrows(); + + /** @brief %Check for unhandled exception specification */ + void unhandledExceptionSpecification(); + + /** @brief %Check for rethrow not from catch scope */ + void rethrowNoCurrentException(); + + /** Don't throw exceptions in destructors */ + void destructorsError(const Token * const tok, const std::string &className); + void deallocThrowError(const Token * const tok, const std::string &varname); + void rethrowCopyError(const Token * const tok, const std::string &varname); + void catchExceptionByValueError(const Token *tok); + void noexceptThrowError(const Token * const tok); + /** Missing exception specification */ + void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname); + /** Rethrow without currently handled exception */ + void rethrowNoCurrentExceptionError(const Token *tok); + }; +} + +void CheckExceptionSafetyImpl::destructors() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -82,7 +123,7 @@ void CheckExceptionSafety::destructors() } } -void CheckExceptionSafety::destructorsError(const Token * const tok, const std::string &className) +void CheckExceptionSafetyImpl::destructorsError(const Token * const tok, const std::string &className) { reportError(tok, Severity::warning, "exceptThrowInDestructor", "Class " + className + " is not safe, destructor throws exception\n" @@ -92,7 +133,7 @@ void CheckExceptionSafety::destructorsError(const Token * const tok, const std:: } -void CheckExceptionSafety::deallocThrow() +void CheckExceptionSafetyImpl::deallocThrow() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -155,7 +196,7 @@ void CheckExceptionSafety::deallocThrow() } } -void CheckExceptionSafety::deallocThrowError(const Token * const tok, const std::string &varname) +void CheckExceptionSafetyImpl::deallocThrowError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::warning, "exceptDeallocThrow", "Exception thrown in invalid state, '" + varname + "' points at deallocated memory.", CWE398, Certainty::normal); @@ -167,7 +208,7 @@ void CheckExceptionSafety::deallocThrowError(const Token * const tok, const std: // throw err; // <- should be just "throw;" // } //--------------------------------------------------------------------------- -void CheckExceptionSafety::checkRethrowCopy() +void CheckExceptionSafetyImpl::checkRethrowCopy() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -200,7 +241,7 @@ void CheckExceptionSafety::checkRethrowCopy() } } -void CheckExceptionSafety::rethrowCopyError(const Token * const tok, const std::string &varname) +void CheckExceptionSafetyImpl::rethrowCopyError(const Token * const tok, const std::string &varname) { reportError(tok, Severity::style, "exceptRethrowCopy", "Throwing a copy of the caught exception instead of rethrowing the original exception.\n" @@ -211,7 +252,7 @@ void CheckExceptionSafety::rethrowCopyError(const Token * const tok, const std:: //--------------------------------------------------------------------------- // try {} catch (std::exception err) {} <- Should be "std::exception& err" //--------------------------------------------------------------------------- -void CheckExceptionSafety::checkCatchExceptionByValue() +void CheckExceptionSafetyImpl::checkCatchExceptionByValue() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -232,7 +273,7 @@ void CheckExceptionSafety::checkCatchExceptionByValue() } } -void CheckExceptionSafety::catchExceptionByValueError(const Token *tok) +void CheckExceptionSafetyImpl::catchExceptionByValueError(const Token *tok) { reportError(tok, Severity::style, "catchExceptionByValue", "Exception should be caught by reference.\n" @@ -284,7 +325,7 @@ static const Token * functionThrows(const Function * function) // void func() throw() { throw x; } // void func() __attribute__((nothrow)); void func() { throw x; } //-------------------------------------------------------------------------- -void CheckExceptionSafety::nothrowThrows() +void CheckExceptionSafetyImpl::nothrowThrows() { logChecker("CheckExceptionSafety::nothrowThrows"); @@ -319,7 +360,7 @@ void CheckExceptionSafety::nothrowThrows() } } -void CheckExceptionSafety::noexceptThrowError(const Token * const tok) +void CheckExceptionSafetyImpl::noexceptThrowError(const Token * const tok) { reportError(tok, Severity::error, "throwInNoexceptFunction", "Exception thrown in function declared not to throw exceptions.", CWE398, Certainty::normal); } @@ -327,7 +368,7 @@ void CheckExceptionSafety::noexceptThrowError(const Token * const tok) //-------------------------------------------------------------------------- // void func() { functionWithExceptionSpecification(); } //-------------------------------------------------------------------------- -void CheckExceptionSafety::unhandledExceptionSpecification() +void CheckExceptionSafetyImpl::unhandledExceptionSpecification() { if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; @@ -356,7 +397,7 @@ void CheckExceptionSafety::unhandledExceptionSpecification() } } -void CheckExceptionSafety::unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) +void CheckExceptionSafetyImpl::unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname) { const std::string str1(tok1 ? tok1->str() : "foo"); const std::list locationList = { tok1, tok2 }; @@ -369,7 +410,7 @@ void CheckExceptionSafety::unhandledExceptionSpecificationError(const Token * co //-------------------------------------------------------------------------- // 7.6.18.4 If no exception is presently being handled, evaluating a throw-expression with no operand calls std​::​​terminate(). //-------------------------------------------------------------------------- -void CheckExceptionSafety::rethrowNoCurrentException() +void CheckExceptionSafetyImpl::rethrowNoCurrentException() { logChecker("CheckExceptionSafety::rethrowNoCurrentException"); const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -400,7 +441,7 @@ void CheckExceptionSafety::rethrowNoCurrentException() } } -void CheckExceptionSafety::rethrowNoCurrentExceptionError(const Token *tok) +void CheckExceptionSafetyImpl::rethrowNoCurrentExceptionError(const Token *tok) { reportError(tok, Severity::error, "rethrowNoCurrentException", "Rethrowing current exception with 'throw;', it seems there is no current exception to rethrow." @@ -408,3 +449,29 @@ void CheckExceptionSafety::rethrowNoCurrentExceptionError(const Token *tok) " More: https://isocpp.org/wiki/faq/exceptions#throw-without-an-object", CWE480, Certainty::normal); } + + +void CheckExceptionSafety::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + if (tokenizer.isC()) + return; + + CheckExceptionSafetyImpl checkExceptionSafety(&tokenizer, tokenizer.getSettings(), errorLogger); + checkExceptionSafety.destructors(); + checkExceptionSafety.deallocThrow(); + checkExceptionSafety.checkRethrowCopy(); + checkExceptionSafety.checkCatchExceptionByValue(); + checkExceptionSafety.nothrowThrows(); + checkExceptionSafety.unhandledExceptionSpecification(); + checkExceptionSafety.rethrowNoCurrentException(); +} + +void CheckExceptionSafety::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckExceptionSafetyImpl c(nullptr, settings, errorLogger); + c.destructorsError(nullptr, "Class"); + c.deallocThrowError(nullptr, "p"); + c.rethrowCopyError(nullptr, "varname"); + c.catchExceptionByValueError(nullptr); + c.noexceptThrowError(nullptr); + c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname"); + c.rethrowNoCurrentExceptionError(nullptr); +} diff --git a/lib/checkexceptionsafety.h b/lib/checkexceptionsafety.h index 5888a2c9f57..08bf199587d 100644 --- a/lib/checkexceptionsafety.h +++ b/lib/checkexceptionsafety.h @@ -23,14 +23,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class Settings; class ErrorLogger; -class Token; - +class Tokenizer; /// @addtogroup Checks /// @{ @@ -47,75 +45,13 @@ class Token; class CPPCHECKLIB CheckExceptionSafety : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckExceptionSafety() : Check(myName()) {} + CheckExceptionSafety() : Check("Exception Safety") {} private: - /** This constructor is used when running checks. */ - CheckExceptionSafety(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (tokenizer.isC()) - return; - - CheckExceptionSafety checkExceptionSafety(&tokenizer, tokenizer.getSettings(), errorLogger); - checkExceptionSafety.destructors(); - checkExceptionSafety.deallocThrow(); - checkExceptionSafety.checkRethrowCopy(); - checkExceptionSafety.checkCatchExceptionByValue(); - checkExceptionSafety.nothrowThrows(); - checkExceptionSafety.unhandledExceptionSpecification(); - checkExceptionSafety.rethrowNoCurrentException(); - } - - /** Don't throw exceptions in destructors */ - void destructors(); - - /** deallocating memory and then throw (dead pointer) */ - void deallocThrow(); - - /** Don't rethrow a copy of the caught exception; use a bare throw instead */ - void checkRethrowCopy(); - - /** @brief %Check for exceptions that are caught by value instead of by reference */ - void checkCatchExceptionByValue(); - - /** @brief %Check for functions that throw that shouldn't */ - void nothrowThrows(); - - /** @brief %Check for unhandled exception specification */ - void unhandledExceptionSpecification(); - - /** @brief %Check for rethrow not from catch scope */ - void rethrowNoCurrentException(); - - /** Don't throw exceptions in destructors */ - void destructorsError(const Token * const tok, const std::string &className); - void deallocThrowError(const Token * const tok, const std::string &varname); - void rethrowCopyError(const Token * const tok, const std::string &varname); - void catchExceptionByValueError(const Token *tok); - void noexceptThrowError(const Token * const tok); - /** Missing exception specification */ - void unhandledExceptionSpecificationError(const Token * const tok1, const Token * const tok2, const std::string & funcname); - /** Rethrow without currently handled exception */ - void rethrowNoCurrentExceptionError(const Token *tok); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** Generate all possible errors (for --errorlist) */ - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckExceptionSafety c(nullptr, settings, errorLogger); - c.destructorsError(nullptr, "Class"); - c.deallocThrowError(nullptr, "p"); - c.rethrowCopyError(nullptr, "varname"); - c.catchExceptionByValueError(nullptr); - c.noexceptThrowError(nullptr); - c.unhandledExceptionSpecificationError(nullptr, nullptr, "funcname"); - c.rethrowNoCurrentExceptionError(nullptr); - } - - /** Short description of class (for --doc) */ - static std::string myName() { - return "Exception Safety"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; /** wiki formatted description of the class (for --doc) */ std::string classInfo() const override { diff --git a/lib/checkfunctions.cpp b/lib/checkfunctions.cpp index cd9b8f980d3..9756ac49c61 100644 --- a/lib/checkfunctions.cpp +++ b/lib/checkfunctions.cpp @@ -23,8 +23,10 @@ #include "checkfunctions.h" #include "astutils.h" +#include "checkimpl.h" #include "mathlib.h" #include "platform.h" +#include "settings.h" #include "standards.h" #include "symboldatabase.h" #include "token.h" @@ -40,12 +42,6 @@ //--------------------------------------------------------------------------- - -// Register this check class (by creating a static instance of it) -namespace { - CheckFunctions instance; -} - static const CWE CWE252(252U); // Unchecked Return Value static const CWE CWE477(477U); // Use of Obsolete Functions static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior @@ -54,7 +50,69 @@ static const CWE CWE686(686U); // Function Call With Incorrect Argument Type static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argument Value static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument -void CheckFunctions::checkProhibitedFunctions() +// Register this check class (by creating a static instance of it) +namespace { + CheckFunctions instance; + + class CheckFunctionsImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckFunctionsImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** Check for functions that should not be used */ + void checkProhibitedFunctions(); + + /** + * @brief Invalid function usage (invalid input value / overlapping data) + * + * %Check that given function parameters are valid according to the standard + * - wrong radix given for strtol/strtoul + * - overlapping data when using sprintf/snprintf + * - wrong input value according to library + */ + void invalidFunctionUsage(); + + /** @brief %Check for ignored return values. */ + void checkIgnoredReturnValue(); + + /** @brief %Check for parameters given to math function that do not make sense*/ + void checkMathFunctions(); + + /** @brief %Check for filling zero bytes with memset() */ + void memsetZeroBytes(); + + /** @brief %Check for invalid 2nd parameter of memset() */ + void memsetInvalid2ndParam(); + + /** @brief %Check for copy elision by RVO|NRVO */ + void returnLocalStdMove(); + + void useStandardLibrary(); + + /** @brief --check-library: warn for unconfigured function calls */ + void checkLibraryMatchFunctions(); + + /** @brief %Check for missing "return" */ + void checkMissingReturn(); + + void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); + void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); + void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr); + void ignoredReturnValueError(const Token* tok, const std::string& function); + void ignoredReturnErrorCode(const Token* tok, const std::string& function); + void mathfunctionCallWarning(const Token *tok, const nonneg int numParam = 1); + void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); + void memsetZeroBytesError(const Token *tok); + void memsetFloatError(const Token *tok, const std::string &var_value); + void memsetValueOutOfRangeError(const Token *tok, const std::string &value); + void missingReturnError(const Token *tok); + void copyElisionError(const Token *tok); + void useStandardLibraryError(const Token *tok, const std::string& expected); + }; +} + +void CheckFunctionsImpl::checkProhibitedFunctions() { const bool checkAlloca = mSettings->severity.isEnabled(Severity::warning) && ((mSettings->standards.c >= Standards::C99 && mTokenizer->isC()) || mSettings->standards.cpp >= Standards::CPP11); @@ -101,7 +159,7 @@ void CheckFunctions::checkProhibitedFunctions() //--------------------------------------------------------------------------- // Check , and //--------------------------------------------------------------------------- -void CheckFunctions::invalidFunctionUsage() +void CheckFunctionsImpl::invalidFunctionUsage() { logChecker("CheckFunctions::invalidFunctionUsage"); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -192,7 +250,7 @@ void CheckFunctions::invalidFunctionUsage() } } -void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) +void CheckFunctionsImpl::invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; @@ -221,7 +279,7 @@ void CheckFunctions::invalidFunctionArgError(const Token *tok, const std::string Certainty::normal); } -void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) +void CheckFunctionsImpl::invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; @@ -229,7 +287,7 @@ void CheckFunctions::invalidFunctionArgBoolError(const Token *tok, const std::st reportError(tok, Severity::error, "invalidFunctionArgBool", errmsg.str(), CWE628, Certainty::normal); } -void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) +void CheckFunctionsImpl::invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr) { std::ostringstream errmsg; errmsg << "$symbol:" << functionName << '\n'; @@ -240,7 +298,7 @@ void CheckFunctions::invalidFunctionArgStrError(const Token *tok, const std::str //--------------------------------------------------------------------------- // Check for ignored return values. //--------------------------------------------------------------------------- -void CheckFunctions::checkIgnoredReturnValue() +void CheckFunctionsImpl::checkIgnoredReturnValue() { if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->severity.isEnabled(Severity::style)) return; @@ -288,13 +346,13 @@ void CheckFunctions::checkIgnoredReturnValue() } } -void CheckFunctions::ignoredReturnValueError(const Token* tok, const std::string& function) +void CheckFunctionsImpl::ignoredReturnValueError(const Token* tok, const std::string& function) { reportError(tok, Severity::warning, "ignoredReturnValue", "$symbol:" + function + "\nReturn value of function $symbol() is not used.", CWE252, Certainty::normal); } -void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& function) +void CheckFunctionsImpl::ignoredReturnErrorCode(const Token* tok, const std::string& function) { reportError(tok, Severity::style, "ignoredReturnErrorCode", "$symbol:" + function + "\nError code from the return value of function $symbol() is not used.", CWE252, Certainty::normal); @@ -305,7 +363,7 @@ void CheckFunctions::ignoredReturnErrorCode(const Token* tok, const std::string& //--------------------------------------------------------------------------- static const Token *checkMissingReturnScope(const Token *tok, const Library &library); -void CheckFunctions::checkMissingReturn() +void CheckFunctionsImpl::checkMissingReturn() { logChecker("CheckFunctions::checkMissingReturn"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -411,7 +469,7 @@ static const Token *checkMissingReturnScope(const Token *tok, const Library &lib return nullptr; } -void CheckFunctions::missingReturnError(const Token* tok) +void CheckFunctionsImpl::missingReturnError(const Token* tok) { reportError(tok, Severity::error, "missingReturn", "Found an exit path from function with non-void return type that has missing return statement", CWE758, Certainty::normal); @@ -419,7 +477,7 @@ void CheckFunctions::missingReturnError(const Token* tok) //--------------------------------------------------------------------------- // Detect passing wrong values to functions like atan(0, x); //--------------------------------------------------------------------------- -void CheckFunctions::checkMathFunctions() +void CheckFunctionsImpl::checkMathFunctions() { const bool styleC99 = mSettings->severity.isEnabled(Severity::style) && mSettings->standards.c != Standards::C89 && mSettings->standards.cpp != Standards::CPP03; const bool printWarnings = mSettings->severity.isEnabled(Severity::warning); @@ -480,7 +538,7 @@ void CheckFunctions::checkMathFunctions() } } -void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) +void CheckFunctionsImpl::mathfunctionCallWarning(const Token *tok, const nonneg int numParam) { if (tok) { if (numParam == 1) @@ -491,7 +549,7 @@ void CheckFunctions::mathfunctionCallWarning(const Token *tok, const nonneg int reportError(tok, Severity::warning, "wrongmathcall", "Passing value '#' to #() leads to implementation-defined result.", CWE758, Certainty::normal); } -void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) +void CheckFunctionsImpl::mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp) { reportError(tok, Severity::style, "unpreciseMathCall", "Expression '" + oldexp + "' can be replaced by '" + newexp + "' to avoid loss of precision.", CWE758, Certainty::normal); } @@ -499,7 +557,7 @@ void CheckFunctions::mathfunctionCallWarning(const Token *tok, const std::string //--------------------------------------------------------------------------- // memset(p, y, 0 /* bytes to fill */) <- 2nd and 3rd arguments inverted //--------------------------------------------------------------------------- -void CheckFunctions::memsetZeroBytes() +void CheckFunctionsImpl::memsetZeroBytes() { // FIXME: // Replace this with library configuration. @@ -528,7 +586,7 @@ void CheckFunctions::memsetZeroBytes() } } -void CheckFunctions::memsetZeroBytesError(const Token *tok) +void CheckFunctionsImpl::memsetZeroBytesError(const Token *tok) { const std::string summary("memset() called to fill 0 bytes."); const std::string verbose(summary + " The second and third arguments might be inverted." @@ -537,7 +595,7 @@ void CheckFunctions::memsetZeroBytesError(const Token *tok) reportError(tok, Severity::warning, "memsetZeroBytes", summary + "\n" + verbose, CWE687, Certainty::normal); } -void CheckFunctions::memsetInvalid2ndParam() +void CheckFunctionsImpl::memsetInvalid2ndParam() { // FIXME: // Replace this with library configuration. @@ -585,7 +643,7 @@ void CheckFunctions::memsetInvalid2ndParam() } } -void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_value) +void CheckFunctionsImpl::memsetFloatError(const Token *tok, const std::string &var_value) { const std::string message("The 2nd memset() argument '" + var_value + "' is a float, its representation is implementation defined."); @@ -594,7 +652,7 @@ void CheckFunctions::memsetFloatError(const Token *tok, const std::string &var_v reportError(tok, Severity::portability, "memsetFloat", message + "\n" + verbose, CWE688, Certainty::normal); } -void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::string &value) +void CheckFunctionsImpl::memsetValueOutOfRangeError(const Token *tok, const std::string &value) { const std::string message("The 2nd memset() argument '" + value + "' doesn't fit into an 'unsigned char'."); const std::string verbose(message + " The 2nd parameter is passed as an 'int', but the function fills the block of memory using the 'unsigned char' conversion of this value."); @@ -605,7 +663,7 @@ void CheckFunctions::memsetValueOutOfRangeError(const Token *tok, const std::str // --check-library => warn for unconfigured functions //--------------------------------------------------------------------------- -void CheckFunctions::checkLibraryMatchFunctions() +void CheckFunctionsImpl::checkLibraryMatchFunctions() { if (!mSettings->checkLibrary) return; @@ -677,7 +735,7 @@ void CheckFunctions::checkLibraryMatchFunctions() // Check for problems to compiler apply (Named) Return Value Optimization for local variable // Technically we have different guarantees between standard versions // details: https://en.cppreference.com/w/cpp/language/copy_elision -void CheckFunctions::returnLocalStdMove() +void CheckFunctionsImpl::returnLocalStdMove() { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) return; @@ -707,7 +765,7 @@ void CheckFunctions::returnLocalStdMove() } } -void CheckFunctions::copyElisionError(const Token *tok) +void CheckFunctionsImpl::copyElisionError(const Token *tok) { reportError(tok, Severity::performance, @@ -716,7 +774,7 @@ void CheckFunctions::copyElisionError(const Token *tok) " More: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rf-return-move-local"); } -void CheckFunctions::useStandardLibrary() +void CheckFunctionsImpl::useStandardLibrary() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -820,9 +878,49 @@ void CheckFunctions::useStandardLibrary() } } -void CheckFunctions::useStandardLibraryError(const Token *tok, const std::string& expected) +void CheckFunctionsImpl::useStandardLibraryError(const Token *tok, const std::string& expected) { reportError(tok, Severity::style, "useStandardLibrary", "Consider using " + expected + " instead of loop."); } + +void CheckFunctions::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckFunctionsImpl checkFunctions(&tokenizer, tokenizer.getSettings(), errorLogger); + + checkFunctions.checkIgnoredReturnValue(); + checkFunctions.checkMissingReturn(); // Missing "return" in exit path + + // --check-library : functions with nonmatching configuration + checkFunctions.checkLibraryMatchFunctions(); + + checkFunctions.checkProhibitedFunctions(); + checkFunctions.invalidFunctionUsage(); + checkFunctions.checkMathFunctions(); + checkFunctions.memsetZeroBytes(); + checkFunctions.memsetInvalid2ndParam(); + checkFunctions.returnLocalStdMove(); + checkFunctions.useStandardLibrary(); +} + +void CheckFunctions::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckFunctionsImpl c(nullptr, settings, errorLogger); + + for (std::map::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) { + // TODO + //c.reportError(nullptr, Severity::style, i->first+"Called", i->second.message); + } + + c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); + c.invalidFunctionArgBoolError(nullptr, "func_name", 1); + c.invalidFunctionArgStrError(nullptr, "func_name", 1); + c.ignoredReturnValueError(nullptr, "malloc"); + c.mathfunctionCallWarning(nullptr); + c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); + c.memsetZeroBytesError(nullptr); + c.memsetFloatError(nullptr, "varname"); + c.memsetValueOutOfRangeError(nullptr, "varname"); + c.missingReturnError(nullptr); + c.copyElisionError(nullptr); + c.useStandardLibraryError(nullptr, "memcpy"); +} diff --git a/lib/checkfunctions.h b/lib/checkfunctions.h index 537f8fe874b..97630c5a07d 100644 --- a/lib/checkfunctions.h +++ b/lib/checkfunctions.h @@ -24,22 +24,12 @@ #include "check.h" #include "config.h" -#include "errortypes.h" -#include "library.h" -#include "settings.h" -#include "tokenize.h" -#include #include -#include -class Token; class ErrorLogger; - -namespace ValueFlow { - class Value; -} // namespace ValueFlow - +class Settings; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -51,106 +41,13 @@ namespace ValueFlow { class CPPCHECKLIB CheckFunctions : public Check { public: /** This constructor is used when registering the CheckFunctions */ - CheckFunctions() : Check(myName()) {} + CheckFunctions() : Check("Check function usage") {} private: - /** This constructor is used when running checks. */ - CheckFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckFunctions checkFunctions(&tokenizer, tokenizer.getSettings(), errorLogger); - - checkFunctions.checkIgnoredReturnValue(); - checkFunctions.checkMissingReturn(); // Missing "return" in exit path - - // --check-library : functions with nonmatching configuration - checkFunctions.checkLibraryMatchFunctions(); - - checkFunctions.checkProhibitedFunctions(); - checkFunctions.invalidFunctionUsage(); - checkFunctions.checkMathFunctions(); - checkFunctions.memsetZeroBytes(); - checkFunctions.memsetInvalid2ndParam(); - checkFunctions.returnLocalStdMove(); - checkFunctions.useStandardLibrary(); - } - - /** Check for functions that should not be used */ - void checkProhibitedFunctions(); - - /** - * @brief Invalid function usage (invalid input value / overlapping data) - * - * %Check that given function parameters are valid according to the standard - * - wrong radix given for strtol/strtoul - * - overlapping data when using sprintf/snprintf - * - wrong input value according to library - */ - void invalidFunctionUsage(); - - /** @brief %Check for ignored return values. */ - void checkIgnoredReturnValue(); - - /** @brief %Check for parameters given to math function that do not make sense*/ - void checkMathFunctions(); - - /** @brief %Check for filling zero bytes with memset() */ - void memsetZeroBytes(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check for invalid 2nd parameter of memset() */ - void memsetInvalid2ndParam(); - - /** @brief %Check for copy elision by RVO|NRVO */ - void returnLocalStdMove(); - - void useStandardLibrary(); - - /** @brief --check-library: warn for unconfigured function calls */ - void checkLibraryMatchFunctions(); - - /** @brief %Check for missing "return" */ - void checkMissingReturn(); - - void invalidFunctionArgError(const Token *tok, const std::string &functionName, int argnr, const ValueFlow::Value *invalidValue, const std::string &validstr); - void invalidFunctionArgBoolError(const Token *tok, const std::string &functionName, int argnr); - void invalidFunctionArgStrError(const Token *tok, const std::string &functionName, nonneg int argnr); - void ignoredReturnValueError(const Token* tok, const std::string& function); - void ignoredReturnErrorCode(const Token* tok, const std::string& function); - void mathfunctionCallWarning(const Token *tok, const nonneg int numParam = 1); - void mathfunctionCallWarning(const Token *tok, const std::string& oldexp, const std::string& newexp); - void memsetZeroBytesError(const Token *tok); - void memsetFloatError(const Token *tok, const std::string &var_value); - void memsetValueOutOfRangeError(const Token *tok, const std::string &value); - void missingReturnError(const Token *tok); - void copyElisionError(const Token *tok); - void useStandardLibraryError(const Token *tok, const std::string& expected); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckFunctions c(nullptr, settings, errorLogger); - - for (std::map::const_iterator i = settings->library.functionwarn.cbegin(); i != settings->library.functionwarn.cend(); ++i) { - c.reportError(nullptr, Severity::style, i->first+"Called", i->second.message); - } - - c.invalidFunctionArgError(nullptr, "func_name", 1, nullptr,"1:4"); - c.invalidFunctionArgBoolError(nullptr, "func_name", 1); - c.invalidFunctionArgStrError(nullptr, "func_name", 1); - c.ignoredReturnValueError(nullptr, "malloc"); - c.mathfunctionCallWarning(nullptr); - c.mathfunctionCallWarning(nullptr, "1 - erf(x)", "erfc(x)"); - c.memsetZeroBytesError(nullptr); - c.memsetFloatError(nullptr, "varname"); - c.memsetValueOutOfRangeError(nullptr, "varname"); - c.missingReturnError(nullptr); - c.copyElisionError(nullptr); - c.useStandardLibraryError(nullptr, "memcpy"); - } - - static std::string myName() { - return "Check function usage"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check function usage:\n" diff --git a/lib/checkimpl.cpp b/lib/checkimpl.cpp new file mode 100644 index 00000000000..ac24644c281 --- /dev/null +++ b/lib/checkimpl.cpp @@ -0,0 +1,83 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "checkimpl.h" + +#include "check.h" + +#include "errorlogger.h" +#include "settings.h" +#include "token.h" +#include "tokenize.h" +#include "vfvalue.h" + +#include +#include +#include +#include +#include + +void CheckImpl::reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) +{ + const ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); + if (mErrorLogger) + mErrorLogger->reportErr(errmsg); + // TODO + //else + // Check::reportError(errmsg); +} + +void CheckImpl::reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty) +{ + const ErrorMessage errmsg(errorPath, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, certainty); + if (mErrorLogger) + mErrorLogger->reportErr(errmsg); + // TODO + //else + // Check::reportError(errmsg); +} + +bool CheckImpl::wrongData(const Token *tok, const char *str) +{ + if (mSettings->daca) + reportError(tok, Severity::debug, "DacaWrongData", "Wrong data detected by condition " + std::string(str)); + return true; +} + +ErrorPath CheckImpl::getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const +{ + ErrorPath errorPath; + if (!value) { + errorPath.emplace_back(errtok, std::move(bug)); + } else if (mSettings->verbose || mSettings->xml || !mSettings->templateLocation.empty()) { + errorPath = value->errorPath; + errorPath.emplace_back(errtok, std::move(bug)); + } else { + if (value->condition) + errorPath.emplace_back(value->condition, "condition '" + value->condition->expressionString() + "'"); + //else if (!value->isKnown() || value->defaultArg) + // errorPath = value->callstack; + errorPath.emplace_back(errtok, std::move(bug)); + } + return errorPath; +} + +void CheckImpl::logChecker(const char id[]) +{ + reportError(nullptr, Severity::none, "logChecker", id); +} diff --git a/lib/checkimpl.h b/lib/checkimpl.h new file mode 100644 index 00000000000..ece61d36d0d --- /dev/null +++ b/lib/checkimpl.h @@ -0,0 +1,81 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2023 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef checkimplH +#define checkimplH + +#include "errortypes.h" +#include "vfvalue.h" + +#include +#include + +class Settings; +class ErrorLogger; +class Tokenizer; +class Check; +class Token; + +class CheckImpl +{ +public: + /** This constructor is used when running checks. */ + CheckImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : mTokenizer(tokenizer), mSettings(settings), mErrorLogger(errorLogger) {} + + CheckImpl(const CheckImpl &) = delete; + Check& operator=(const CheckImpl &) = delete; + +protected: + const Tokenizer* const mTokenizer{}; + const Settings* const mSettings{}; + ErrorLogger* const mErrorLogger{}; + + /** report an error */ + void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg) { + reportError(tok, severity, id, msg, CWE(0U), Certainty::normal); + } + + /** report an error */ + void reportError(const Token *tok, const Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty) { + const std::list callstack(1, tok); + reportError(callstack, severity, id, msg, cwe, certainty); + } + + /** report an error */ + void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg) { + reportError(callstack, severity, id, msg, CWE(0U), Certainty::normal); + } + + /** report an error */ + void reportError(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe, Certainty certainty); + + void reportError(const ErrorPath &errorPath, Severity severity, const char id[], const std::string &msg, const CWE &cwe, Certainty certainty); + + ErrorPath getErrorPath(const Token* errtok, const ValueFlow::Value* value, std::string bug) const; + + /** + * Use WRONG_DATA in checkers when you check for wrong data. That + * will call this method + */ + bool wrongData(const Token *tok, const char *str); + + void logChecker(const char id[]); +}; + +#endif // checkimplH diff --git a/lib/checkinternal.cpp b/lib/checkinternal.cpp index b152389bad5..1e117159bf5 100644 --- a/lib/checkinternal.cpp +++ b/lib/checkinternal.cpp @@ -21,6 +21,8 @@ #include "checkinternal.h" #include "astutils.h" +#include "checkimpl.h" +#include "settings.h" #include "symboldatabase.h" #include "token.h" #include "tokenize.h" @@ -33,9 +35,47 @@ // Disabled in release builds namespace { CheckInternal instance; + + class CheckInternalImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckInternalImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ + void checkTokenMatchPatterns(); + + /** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */ + void checkTokenSimpleMatchPatterns(); + + /** @brief %Check for missing % end character in Token::Match pattern */ + void checkMissingPercentCharacter(); + + /** @brief %Check for unknown (invalid) complex patterns like "%typ%" */ + void checkUnknownPattern(); + + /** @brief %Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt() */ + void checkRedundantNextPrevious(); + + /** @brief %Check if there is whitespace at the beginning or at the end of a pattern */ + void checkExtraWhitespace(); + + /** @brief %Check if there is a redundant check for none-nullness of parameter before Match functions, such as (tok && Token::Match(tok, "foo")) */ + void checkRedundantTokCheck(); + + void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); + void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); + void unknownPatternError(const Token* tok, const std::string& pattern); + void redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2); + void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname); + void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname); + void checkRedundantTokCheckError(const Token *tok); + }; } -void CheckInternal::checkTokenMatchPatterns() +void CheckInternalImpl::checkTokenMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { @@ -86,7 +126,7 @@ void CheckInternal::checkTokenMatchPatterns() } } -void CheckInternal::checkRedundantTokCheck() +void CheckInternalImpl::checkRedundantTokCheck() { for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (Token::Match(tok, "&& Token :: simpleMatch|Match|findsimplematch|findmatch (")) { @@ -121,13 +161,13 @@ void CheckInternal::checkRedundantTokCheck() } -void CheckInternal::checkRedundantTokCheckError(const Token* tok) +void CheckInternalImpl::checkRedundantTokCheckError(const Token* tok) { reportError(tok, Severity::style, "redundantTokCheck", "Unnecessary check of \"" + (tok? tok->expressionString(): emptyString) + "\", match-function already checks if it is null."); } -void CheckInternal::checkTokenSimpleMatchPatterns() +void CheckInternalImpl::checkTokenSimpleMatchPatterns() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { @@ -211,7 +251,7 @@ namespace { }; } -void CheckInternal::checkMissingPercentCharacter() +void CheckInternalImpl::checkMissingPercentCharacter() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { @@ -253,7 +293,7 @@ void CheckInternal::checkMissingPercentCharacter() } } -void CheckInternal::checkUnknownPattern() +void CheckInternalImpl::checkUnknownPattern() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { @@ -287,7 +327,7 @@ void CheckInternal::checkUnknownPattern() } } -void CheckInternal::checkRedundantNextPrevious() +void CheckInternalImpl::checkRedundantNextPrevious() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { @@ -318,7 +358,7 @@ void CheckInternal::checkRedundantNextPrevious() } } -void CheckInternal::checkExtraWhitespace() +void CheckInternalImpl::checkExtraWhitespace() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* scope : symbolDatabase->functionScopes) { @@ -344,57 +384,85 @@ void CheckInternal::checkExtraWhitespace() } } -void CheckInternal::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +void CheckInternalImpl::multiComparePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "multiComparePatternError", "Bad multicompare pattern (a %cmd% must be first unless it is %or%,%op%,%cop%,%name%,%oror%) inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } -void CheckInternal::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +void CheckInternalImpl::simplePatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "simplePatternError", "Found simple pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } -void CheckInternal::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname) +void CheckInternalImpl::complexPatternError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "complexPatternError", "Found complex pattern inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } -void CheckInternal::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname) +void CheckInternalImpl::missingPercentCharacterError(const Token* tok, const std::string& pattern, const std::string& funcname) { reportError(tok, Severity::error, "missingPercentCharacter", "Missing percent end character in Token::" + funcname + "() pattern: \"" + pattern + "\"" ); } -void CheckInternal::unknownPatternError(const Token* tok, const std::string& pattern) +void CheckInternalImpl::unknownPatternError(const Token* tok, const std::string& pattern) { reportError(tok, Severity::error, "unknownPattern", "Unknown pattern used: \"" + pattern + "\""); } -void CheckInternal::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2) +void CheckInternalImpl::redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2) { reportError(tok, Severity::style, "redundantNextPrevious", "Call to 'Token::" + func1 + "()' followed by 'Token::" + func2 + "()' can be simplified."); } -void CheckInternal::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname) +void CheckInternalImpl::orInComplexPattern(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::error, "orInComplexPattern", "Token::" + funcname + "() pattern \"" + pattern + "\" contains \"||\" or \"|\". Replace it by \"%oror%\" or \"%or%\"."); } -void CheckInternal::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname) +void CheckInternalImpl::extraWhitespaceError(const Token* tok, const std::string& pattern, const std::string &funcname) { reportError(tok, Severity::warning, "extraWhitespaceError", "Found extra whitespace inside Token::" + funcname + "() call: \"" + pattern + "\"" ); } +void CheckInternal::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + if (!tokenizer.getSettings()->checks.isEnabled(Checks::internalCheck)) + return; + + CheckInternalImpl checkInternal(&tokenizer, tokenizer.getSettings(), errorLogger); + + checkInternal.checkTokenMatchPatterns(); + checkInternal.checkTokenSimpleMatchPatterns(); + checkInternal.checkMissingPercentCharacter(); + checkInternal.checkUnknownPattern(); + checkInternal.checkRedundantNextPrevious(); + checkInternal.checkExtraWhitespace(); + checkInternal.checkRedundantTokCheck(); +} + +void CheckInternal::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckInternalImpl c(nullptr, settings, errorLogger); + c.multiComparePatternError(nullptr, ";|%type%", "Match"); + c.simplePatternError(nullptr, "class {", "Match"); + c.complexPatternError(nullptr, "%type% ( )", "Match"); + c.missingPercentCharacterError(nullptr, "%num", "Match"); + c.unknownPatternError(nullptr, "%typ"); + c.redundantNextPreviousError(nullptr, "previous", "next"); + c.orInComplexPattern(nullptr, "||", "Match"); + c.extraWhitespaceError(nullptr, "%str% ", "Match"); + c.checkRedundantTokCheckError(nullptr); +} + #endif // #ifdef CHECK_INTERNAL diff --git a/lib/checkinternal.h b/lib/checkinternal.h index 2ce0a2649ff..372cec1671e 100644 --- a/lib/checkinternal.h +++ b/lib/checkinternal.h @@ -24,14 +24,12 @@ #include "check.h" #include "config.h" -#include "errortypes.h" -#include "settings.h" -#include "tokenize.h" #include class ErrorLogger; -class Token; +class Settings; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -41,75 +39,12 @@ class Token; class CPPCHECKLIB CheckInternal : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckInternal() : Check(myName()) {} + CheckInternal() : Check("cppcheck internal API usage") {} private: - /** This constructor is used when running checks. */ - CheckInternal(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (!tokenizer.getSettings()->checks.isEnabled(Checks::internalCheck)) - return; - - CheckInternal checkInternal(&tokenizer, tokenizer.getSettings(), errorLogger); - - checkInternal.checkTokenMatchPatterns(); - checkInternal.checkTokenSimpleMatchPatterns(); - checkInternal.checkMissingPercentCharacter(); - checkInternal.checkUnknownPattern(); - checkInternal.checkRedundantNextPrevious(); - checkInternal.checkExtraWhitespace(); - checkInternal.checkRedundantTokCheck(); - } - - /** @brief %Check if a simple pattern is used inside Token::Match or Token::findmatch */ - void checkTokenMatchPatterns(); - - /** @brief %Check if a complex pattern is used inside Token::simpleMatch or Token::findsimplematch */ - void checkTokenSimpleMatchPatterns(); - - /** @brief %Check for missing % end character in Token::Match pattern */ - void checkMissingPercentCharacter(); - - /** @brief %Check for unknown (invalid) complex patterns like "%typ%" */ - void checkUnknownPattern(); - - /** @brief %Check for inefficient usage of Token::next(), Token::previous() and Token::tokAt() */ - void checkRedundantNextPrevious(); - - /** @brief %Check if there is whitespace at the beginning or at the end of a pattern */ - void checkExtraWhitespace(); - - /** @brief %Check if there is a redundant check for none-nullness of parameter before Match functions, such as (tok && Token::Match(tok, "foo")) */ - void checkRedundantTokCheck(); - - void multiComparePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); - void simplePatternError(const Token *tok, const std::string &pattern, const std::string &funcname); - void complexPatternError(const Token *tok, const std::string &pattern, const std::string &funcname); - void missingPercentCharacterError(const Token *tok, const std::string &pattern, const std::string &funcname); - void unknownPatternError(const Token* tok, const std::string& pattern); - void redundantNextPreviousError(const Token* tok, const std::string& func1, const std::string& func2); - void orInComplexPattern(const Token *tok, const std::string &pattern, const std::string &funcname); - void extraWhitespaceError(const Token *tok, const std::string &pattern, const std::string &funcname); - void checkRedundantTokCheckError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckInternal c(nullptr, settings, errorLogger); - c.multiComparePatternError(nullptr, ";|%type%", "Match"); - c.simplePatternError(nullptr, "class {", "Match"); - c.complexPatternError(nullptr, "%type% ( )", "Match"); - c.missingPercentCharacterError(nullptr, "%num", "Match"); - c.unknownPatternError(nullptr, "%typ"); - c.redundantNextPreviousError(nullptr, "previous", "next"); - c.orInComplexPattern(nullptr, "||", "Match"); - c.extraWhitespaceError(nullptr, "%str% ", "Match"); - c.checkRedundantTokCheckError(nullptr); - } - - static std::string myName() { - return "cppcheck internal API usage"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { // Don't include these checks on the WIKI where people can read what diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 2d52f9ad4e1..748b060709b 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -19,6 +19,7 @@ //--------------------------------------------------------------------------- #include "checkio.h" +#include "checkimpl.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" @@ -44,11 +45,6 @@ //--------------------------------------------------------------------------- -// Register CheckIO.. -namespace { - CheckIO instance; -} - // CVE ID used: static const CWE CWE119(119U); // Improper Restriction of Operations within the Bounds of a Memory Buffer static const CWE CWE398(398U); // Indicator of Poor Code Quality @@ -59,10 +55,95 @@ static const CWE CWE687(687U); // Function Call With Incorrectly Specified Argu static const CWE CWE704(704U); // Incorrect Type Conversion or Cast static const CWE CWE910(910U); // Use of Expired File Descriptor +// Register CheckIO.. +namespace { + CheckIO instance; + + class CheckIOImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckIOImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for missusage of std::cout */ + void checkCoutCerrMisusage(); + + /** @brief %Check usage of files*/ + void checkFileUsage(); + + /** @brief scanf can crash if width specifiers are not used */ + void invalidScanf(); + + /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ + void checkWrongPrintfScanfArguments(); + + class ArgumentInfo { + public: + ArgumentInfo(const Token *arg, const Settings *settings, bool _isCPP); + ~ArgumentInfo(); + + ArgumentInfo(const ArgumentInfo &) = delete; + ArgumentInfo& operator= (const ArgumentInfo &) = delete; + + bool isArrayOrPointer() const; + bool isComplexType() const; + bool isKnownType() const; + bool isStdVectorOrString(); + bool isStdContainer(const Token *tok); + bool isLibraryType(const Settings *settings) const; + + const Variable* variableInfo{}; + const Token* typeToken{}; + const Function* functionInfo{}; + Token* tempToken{}; + bool element{}; + bool _template{}; + bool address{}; + bool isCPP{}; + }; + + void checkFormatString(const Token * const tok, + const Token * const formatStringTok, + const Token * argListTok, + const bool scan, + const bool scanf_s); + + // Reporting errors.. + void coutCerrMisusageError(const Token* tok, const std::string& streamName); + void fflushOnInputStreamError(const Token *tok, const std::string &varname); + void ioWithoutPositioningError(const Token *tok); + void readWriteOnlyFileError(const Token *tok); + void writeReadOnlyFileError(const Token *tok); + void useClosedFileError(const Token *tok); + void seekOnAppendedFileError(const Token *tok); + void incompatibleFileOpenError(const Token *tok, const std::string &filename); + void invalidScanfError(const Token *tok); + void wrongPrintfScanfArgumentsError(const Token* tok, + const std::string &functionName, + nonneg int numFormat, + nonneg int numFunction); + void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, + nonneg int index, nonneg int numFunction); + void invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); + void invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); + void invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier); + void invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier); + static void argumentType(std::ostream & os, const ArgumentInfo * argInfo); + static Severity getSeverity(const ArgumentInfo *argInfo); + }; +} + //--------------------------------------------------------------------------- // std::cout << std::cout; //--------------------------------------------------------------------------- -void CheckIO::checkCoutCerrMisusage() +void CheckIOImpl::checkCoutCerrMisusage() { if (mTokenizer->isC()) return; @@ -84,7 +165,7 @@ void CheckIO::checkCoutCerrMisusage() } } -void CheckIO::coutCerrMisusageError(const Token* tok, const std::string& streamName) +void CheckIOImpl::coutCerrMisusageError(const Token* tok, const std::string& streamName) { reportError(tok, Severity::error, "coutCerrMisusage", "Invalid usage of output stream: '<< std::" + streamName + "'.", CWE398, Certainty::normal); } @@ -123,7 +204,7 @@ namespace { const std::unordered_set whitelist = { "clearerr", "feof", "ferror", "fgetpos", "ftell", "setbuf", "setvbuf", "ungetc", "ungetwc" }; } -void CheckIO::checkFileUsage() +void CheckIOImpl::checkFileUsage() { const bool windows = mSettings->platform.isWindows(); const bool printPortability = mSettings->severity.isEnabled(Severity::portability); @@ -358,43 +439,43 @@ void CheckIO::checkFileUsage() } } -void CheckIO::fflushOnInputStreamError(const Token *tok, const std::string &varname) +void CheckIOImpl::fflushOnInputStreamError(const Token *tok, const std::string &varname) { reportError(tok, Severity::portability, "fflushOnInputStream", "fflush() called on input stream '" + varname + "' may result in undefined behaviour on non-linux systems.", CWE398, Certainty::normal); } -void CheckIO::ioWithoutPositioningError(const Token *tok) +void CheckIOImpl::ioWithoutPositioningError(const Token *tok) { reportError(tok, Severity::error, "IOWithoutPositioning", "Read and write operations without a call to a positioning function (fseek, fsetpos or rewind) or fflush in between result in undefined behaviour.", CWE664, Certainty::normal); } -void CheckIO::readWriteOnlyFileError(const Token *tok) +void CheckIOImpl::readWriteOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "readWriteOnlyFile", "Read operation on a file that was opened only for writing.", CWE664, Certainty::normal); } -void CheckIO::writeReadOnlyFileError(const Token *tok) +void CheckIOImpl::writeReadOnlyFileError(const Token *tok) { reportError(tok, Severity::error, "writeReadOnlyFile", "Write operation on a file that was opened only for reading.", CWE664, Certainty::normal); } -void CheckIO::useClosedFileError(const Token *tok) +void CheckIOImpl::useClosedFileError(const Token *tok) { reportError(tok, Severity::error, "useClosedFile", "Used file that is not opened.", CWE910, Certainty::normal); } -void CheckIO::seekOnAppendedFileError(const Token *tok) +void CheckIOImpl::seekOnAppendedFileError(const Token *tok) { reportError(tok, Severity::warning, "seekOnAppendedFile", "Repositioning operation performed on a file opened in append mode has no effect.", CWE398, Certainty::normal); } -void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &filename) +void CheckIOImpl::incompatibleFileOpenError(const Token *tok, const std::string &filename) { reportError(tok, Severity::warning, "incompatibleFileOpen", "The file '" + filename + "' is opened for read and write access at the same time on different streams", CWE664, Certainty::normal); @@ -404,7 +485,7 @@ void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &fil //--------------------------------------------------------------------------- // scanf without field width limits can crash with huge input data //--------------------------------------------------------------------------- -void CheckIO::invalidScanf() +void CheckIOImpl::invalidScanf() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -449,7 +530,7 @@ void CheckIO::invalidScanf() } } -void CheckIO::invalidScanfError(const Token *tok) +void CheckIOImpl::invalidScanfError(const Token *tok) { const std::string fname = (tok ? tok->str() : std::string("scanf")); reportError(tok, Severity::warning, @@ -521,7 +602,7 @@ static inline bool typesMatch(const std::string& iToTest, const std::string& iTy return (iToTest == iTypename) || (iToTest == iOptionalPrefix + iTypename); } -void CheckIO::checkWrongPrintfScanfArguments() +void CheckIOImpl::checkWrongPrintfScanfArguments() { const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); const bool isWindows = mSettings->platform.isWindows(); @@ -593,7 +674,7 @@ void CheckIO::checkWrongPrintfScanfArguments() } } -void CheckIO::checkFormatString(const Token * const tok, +void CheckIOImpl::checkFormatString(const Token * const tok, const Token * const formatStringTok, const Token * argListTok, const bool scan, @@ -1337,7 +1418,7 @@ void CheckIO::checkFormatString(const Token * const tok, // We currently only support string literals, variables, and functions. /// @todo add non-string literals, and generic expressions -CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP) +CheckIOImpl::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, bool _isCPP) : isCPP(_isCPP) { if (!arg) @@ -1558,7 +1639,7 @@ CheckIO::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings *settings, } } -CheckIO::ArgumentInfo::~ArgumentInfo() +CheckIOImpl::ArgumentInfo::~ArgumentInfo() { if (tempToken) { while (tempToken->next()) @@ -1573,7 +1654,7 @@ namespace { const std::set stl_string = { "string", "u16string", "u32string", "wstring" }; } -bool CheckIO::ArgumentInfo::isStdVectorOrString() +bool CheckIOImpl::ArgumentInfo::isStdVectorOrString() { if (!isCPP) return false; @@ -1639,7 +1720,7 @@ static const std::set stl_container = { "unordered_map", "unordered_multimap", "unordered_multiset", "unordered_set", "vector" }; -bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) +bool CheckIOImpl::ArgumentInfo::isStdContainer(const Token *tok) { if (!isCPP) return false; @@ -1671,7 +1752,7 @@ bool CheckIO::ArgumentInfo::isStdContainer(const Token *tok) return false; } -bool CheckIO::ArgumentInfo::isArrayOrPointer() const +bool CheckIOImpl::ArgumentInfo::isArrayOrPointer() const { if (address) return true; @@ -1684,7 +1765,7 @@ bool CheckIO::ArgumentInfo::isArrayOrPointer() const return tok && tok->strAt(1) == "*"; } -bool CheckIO::ArgumentInfo::isComplexType() const +bool CheckIOImpl::ArgumentInfo::isComplexType() const { if (variableInfo->type()) return (true); @@ -1696,7 +1777,7 @@ bool CheckIO::ArgumentInfo::isComplexType() const return ((variableInfo->isStlStringType() || (varTypeTok->strAt(1) == "<" && varTypeTok->linkAt(1) && varTypeTok->linkAt(1)->strAt(1) != "::")) && !variableInfo->isArrayOrPointer()); } -bool CheckIO::ArgumentInfo::isKnownType() const +bool CheckIOImpl::ArgumentInfo::isKnownType() const { if (variableInfo) return (typeToken->isStandardType() || typeToken->next()->isStandardType() || isComplexType()); @@ -1706,12 +1787,12 @@ bool CheckIO::ArgumentInfo::isKnownType() const return typeToken->isStandardType() || Token::Match(typeToken, "std :: string|wstring"); } -bool CheckIO::ArgumentInfo::isLibraryType(const Settings *settings) const +bool CheckIOImpl::ArgumentInfo::isLibraryType(const Settings *settings) const { return typeToken && typeToken->isStandardType() && settings->library.podtype(typeToken->str()); } -void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, +void CheckIOImpl::wrongPrintfScanfArgumentsError(const Token* tok, const std::string &functionName, nonneg int numFormat, nonneg int numFunction) @@ -1733,7 +1814,7 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, reportError(tok, severity, "wrongPrintfScanfArgNum", errmsg.str(), CWE685, Certainty::normal); } -void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, +void CheckIOImpl::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction) { if (!mSettings->severity.isEnabled(Severity::warning)) @@ -1748,7 +1829,7 @@ void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, cons reportError(tok, Severity::warning, "wrongPrintfScanfParameterPositionError", errmsg.str(), CWE685, Certainty::normal); } -void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1764,7 +1845,7 @@ void CheckIO::invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, errmsg << "."; reportError(tok, severity, "invalidScanfArgType_s", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) +void CheckIOImpl::invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1809,7 +1890,7 @@ void CheckIO::invalidScanfArgTypeError_int(const Token* tok, nonneg int numForma errmsg << "."; reportError(tok, severity, "invalidScanfArgType_int", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1828,7 +1909,7 @@ void CheckIO::invalidScanfArgTypeError_float(const Token* tok, nonneg int numFor reportError(tok, severity, "invalidScanfArgType_float", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1839,7 +1920,7 @@ void CheckIO::invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_s", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1850,7 +1931,7 @@ void CheckIO::invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_n", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1900,7 +1981,7 @@ static void printfFormatType(std::ostream& os, const std::string& specifier, boo os << "\'"; } -void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1914,7 +1995,7 @@ void CheckIO::invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFor reportError(tok, severity, "invalidPrintfArgType_uint", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1927,7 +2008,7 @@ void CheckIO::invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFor errmsg << "."; reportError(tok, severity, "invalidPrintfArgType_sint", errmsg.str(), CWE686, Certainty::normal); } -void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) +void CheckIOImpl::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo) { const Severity severity = getSeverity(argInfo); if (!mSettings->severity.isEnabled(severity)) @@ -1942,12 +2023,12 @@ void CheckIO::invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFo reportError(tok, severity, "invalidPrintfArgType_float", errmsg.str(), CWE686, Certainty::normal); } -Severity CheckIO::getSeverity(const CheckIO::ArgumentInfo *argInfo) +Severity CheckIOImpl::getSeverity(const ArgumentInfo *argInfo) { return (argInfo && argInfo->typeToken && !argInfo->typeToken->originalName().empty()) ? Severity::portability : Severity::warning; } -void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) +void CheckIOImpl::argumentType(std::ostream& os, const ArgumentInfo * argInfo) { if (argInfo) { os << "\'"; @@ -1997,7 +2078,7 @@ void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) os << "Unknown"; } -void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) +void CheckIOImpl::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -2006,7 +2087,7 @@ void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, reportError(tok, Severity::warning, "invalidLengthModifierError", errmsg.str(), CWE704, Certainty::normal); } -void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier) +void CheckIOImpl::invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier) { MathLib::bigint arrlen = 0; std::string varname; @@ -2029,3 +2110,45 @@ void CheckIO::invalidScanfFormatWidthError(const Token* tok, nonneg int numForma reportError(tok, Severity::error, "invalidScanfFormatWidth", errmsg.str(), CWE687, Certainty::normal); } } + +void CheckIO::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckIOImpl checkIO(&tokenizer, tokenizer.getSettings(), errorLogger); + + checkIO.checkWrongPrintfScanfArguments(); + checkIO.checkCoutCerrMisusage(); + checkIO.checkFileUsage(); + checkIO.invalidScanf(); +} + +void CheckIO::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckIOImpl c(nullptr, settings, errorLogger); + c.coutCerrMisusageError(nullptr, "cout"); + c.fflushOnInputStreamError(nullptr, "stdin"); + c.ioWithoutPositioningError(nullptr); + c.readWriteOnlyFileError(nullptr); + c.writeReadOnlyFileError(nullptr); + c.useClosedFileError(nullptr); + c.seekOnAppendedFileError(nullptr); + c.incompatibleFileOpenError(nullptr, "tmp"); + c.invalidScanfError(nullptr); + c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); + c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); + c.invalidScanfArgTypeError_int(nullptr, 1, "d", nullptr, false); + c.invalidScanfArgTypeError_float(nullptr, 1, "f", nullptr); + c.invalidPrintfArgTypeError_s(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_n(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_p(nullptr, 1, nullptr); + c.invalidPrintfArgTypeError_uint(nullptr, 1, "u", nullptr); + c.invalidPrintfArgTypeError_sint(nullptr, 1, "i", nullptr); + c.invalidPrintfArgTypeError_float(nullptr, 1, "f", nullptr); + c.invalidLengthModifierError(nullptr, 1, "I"); + c.invalidScanfFormatWidthError(nullptr, 10, 5, nullptr, "s"); + c.invalidScanfFormatWidthError(nullptr, 99, -1, nullptr, "s"); + c.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); +} + +void CheckIO::checkWrongPrintfScanfArguments(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckIOImpl c(tokenizer, settings, errorLogger); + c.checkWrongPrintfScanfArguments(); +} diff --git a/lib/checkio.h b/lib/checkio.h index 1293bc0de66..1e83cf85f0a 100644 --- a/lib/checkio.h +++ b/lib/checkio.h @@ -23,17 +23,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" -#include #include -class Function; class Settings; -class Token; -class Variable; class ErrorLogger; -enum class Severity; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -44,126 +39,13 @@ class CPPCHECKLIB CheckIO : public Check { public: /** @brief This constructor is used when registering CheckIO */ - CheckIO() : Check(myName()) {} + CheckIO() : Check("IO using format string") {} private: - /** @brief This constructor is used when running checks. */ - CheckIO(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks on the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckIO checkIO(&tokenizer, tokenizer.getSettings(), errorLogger); - - checkIO.checkWrongPrintfScanfArguments(); - checkIO.checkCoutCerrMisusage(); - checkIO.checkFileUsage(); - checkIO.invalidScanf(); - } - - /** @brief %Check for missusage of std::cout */ - void checkCoutCerrMisusage(); - - /** @brief %Check usage of files*/ - void checkFileUsage(); - - /** @brief scanf can crash if width specifiers are not used */ - void invalidScanf(); - - /** @brief %Checks type and number of arguments given to functions like printf or scanf*/ - void checkWrongPrintfScanfArguments(); - - class ArgumentInfo { - public: - ArgumentInfo(const Token *arg, const Settings *settings, bool _isCPP); - ~ArgumentInfo(); - - ArgumentInfo(const ArgumentInfo &) = delete; - ArgumentInfo& operator= (const ArgumentInfo &) = delete; + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - bool isArrayOrPointer() const; - bool isComplexType() const; - bool isKnownType() const; - bool isStdVectorOrString(); - bool isStdContainer(const Token *tok); - bool isLibraryType(const Settings *settings) const; - - const Variable* variableInfo{}; - const Token* typeToken{}; - const Function* functionInfo{}; - Token* tempToken{}; - bool element{}; - bool _template{}; - bool address{}; - bool isCPP{}; - }; - - void checkFormatString(const Token * const tok, - const Token * const formatStringTok, - const Token * argListTok, - const bool scan, - const bool scanf_s); - - // Reporting errors.. - void coutCerrMisusageError(const Token* tok, const std::string& streamName); - void fflushOnInputStreamError(const Token *tok, const std::string &varname); - void ioWithoutPositioningError(const Token *tok); - void readWriteOnlyFileError(const Token *tok); - void writeReadOnlyFileError(const Token *tok); - void useClosedFileError(const Token *tok); - void seekOnAppendedFileError(const Token *tok); - void incompatibleFileOpenError(const Token *tok, const std::string &filename); - void invalidScanfError(const Token *tok); - void wrongPrintfScanfArgumentsError(const Token* tok, - const std::string &functionName, - nonneg int numFormat, - nonneg int numFunction); - void wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, - nonneg int index, nonneg int numFunction); - void invalidScanfArgTypeError_s(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); - void invalidScanfArgTypeError_int(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo, bool isUnsigned); - void invalidScanfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_s(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_n(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_p(const Token* tok, nonneg int numFormat, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_uint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_sint(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); - void invalidPrintfArgTypeError_float(const Token* tok, nonneg int numFormat, const std::string& specifier, const ArgumentInfo* argInfo); - void invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier); - void invalidScanfFormatWidthError(const Token* tok, nonneg int numFormat, int width, const Variable *var, const std::string& specifier); - static void argumentType(std::ostream & os, const ArgumentInfo * argInfo); - static Severity getSeverity(const ArgumentInfo *argInfo); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckIO c(nullptr, settings, errorLogger); - c.coutCerrMisusageError(nullptr, "cout"); - c.fflushOnInputStreamError(nullptr, "stdin"); - c.ioWithoutPositioningError(nullptr); - c.readWriteOnlyFileError(nullptr); - c.writeReadOnlyFileError(nullptr); - c.useClosedFileError(nullptr); - c.seekOnAppendedFileError(nullptr); - c.incompatibleFileOpenError(nullptr, "tmp"); - c.invalidScanfError(nullptr); - c.wrongPrintfScanfArgumentsError(nullptr, "printf",3,2); - c.invalidScanfArgTypeError_s(nullptr, 1, "s", nullptr); - c.invalidScanfArgTypeError_int(nullptr, 1, "d", nullptr, false); - c.invalidScanfArgTypeError_float(nullptr, 1, "f", nullptr); - c.invalidPrintfArgTypeError_s(nullptr, 1, nullptr); - c.invalidPrintfArgTypeError_n(nullptr, 1, nullptr); - c.invalidPrintfArgTypeError_p(nullptr, 1, nullptr); - c.invalidPrintfArgTypeError_uint(nullptr, 1, "u", nullptr); - c.invalidPrintfArgTypeError_sint(nullptr, 1, "i", nullptr); - c.invalidPrintfArgTypeError_float(nullptr, 1, "f", nullptr); - c.invalidLengthModifierError(nullptr, 1, "I"); - c.invalidScanfFormatWidthError(nullptr, 10, 5, nullptr, "s"); - c.invalidScanfFormatWidthError(nullptr, 99, -1, nullptr, "s"); - c.wrongPrintfScanfPosixParameterPositionError(nullptr, "printf", 2, 1); - } - - static std::string myName() { - return "IO using format string"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check format string input/output operations.\n" @@ -178,6 +60,9 @@ class CPPCHECKLIB CheckIO : public Check { "- Invalid usage of output stream. For example: 'std::cout << std::cout;'\n" "- Wrong number of arguments given to 'printf' or 'scanf;'\n"; } + + // for testing + static void checkWrongPrintfScanfArguments(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkleakautovar.cpp b/lib/checkleakautovar.cpp index d99a28d030d..ba7e747861d 100644 --- a/lib/checkleakautovar.cpp +++ b/lib/checkleakautovar.cpp @@ -23,6 +23,7 @@ #include "checkleakautovar.h" #include "astutils.h" +#include "checkimpl.h" #include "checkmemoryleak.h" // <- CheckMemoryLeak::memoryLeak #include "checknullpointer.h" // <- CheckNullPointer::isPointerDeRef #include "mathlib.h" @@ -45,11 +46,6 @@ //--------------------------------------------------------------------------- -// Register this check class (by creating a static instance of it) -namespace { - CheckLeakAutoVar instance; -} - static const CWE CWE672(672U); static const CWE CWE415(415U); @@ -60,6 +56,119 @@ static constexpr int NEW = -1; static const std::array, 4> alloc_failed_conds {{{"==", "0"}, {"<", "0"}, {"==", "-1"}, {"<=", "-1"}}}; static const std::array, 4> alloc_success_conds {{{"!=", "0"}, {">", "0"}, {"!=", "-1"}, {">=", "0"}}}; +namespace { + class VarInfo { + public: + enum AllocStatus { REALLOC = -3, OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; + struct AllocInfo { + AllocStatus status; + /** Allocation type. If it is a positive value then it corresponds to + * a Library allocation id. A negative value is a builtin + * checkleakautovar allocation type. + */ + int type; + int reallocedFromType = -1; + const Token * allocTok; + explicit AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} + + bool managed() const { + return status < 0; + } + }; + enum Usage { USED, NORET }; + std::map alloctype; + std::map> possibleUsage; + std::set conditionalAlloc; + std::set referenced; + + void clear() { + alloctype.clear(); + possibleUsage.clear(); + conditionalAlloc.clear(); + referenced.clear(); + } + + void erase(nonneg int varid) { + alloctype.erase(varid); + possibleUsage.erase(varid); + conditionalAlloc.erase(varid); + referenced.erase(varid); + } + + void swap(VarInfo &other) { + alloctype.swap(other.alloctype); + possibleUsage.swap(other.possibleUsage); + conditionalAlloc.swap(other.conditionalAlloc); + referenced.swap(other.referenced); + } + + void reallocToAlloc(nonneg int varid) { + const AllocInfo& alloc = alloctype[varid]; + if (alloc.reallocedFromType >= 0) { + const std::map::iterator it = alloctype.find(alloc.reallocedFromType); + if (it != alloctype.end() && it->second.status == REALLOC) { + it->second.status = ALLOC; + } + } + } + + /** set possible usage for all variables */ + void possibleUsageAll(const std::pair& functionUsage); + + void print(); + }; + + // Register this check class (by creating a static instance of it) + CheckLeakAutoVar instance; + + class CheckLeakAutoVarImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckLeakAutoVarImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** check for leaks in all scopes */ + void check(); + + /** check for leaks in a function scope */ + bool checkScope(const Token * const startToken, + VarInfo &varInfo, + std::set notzero, + nonneg int recursiveCount); + + /** Check token inside expression. + * @param tok token inside expression. + * @param varInfo Variable info + * @return next token to process (if no other checks needed for this token). NULL if other checks could be performed. + */ + const Token * checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo); + + /** parse function call */ + void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); + + /** parse changes in allocation status */ + void changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); + + /** update allocation status if reallocation function */ + void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const; + + /** return. either "return" or end of variable scope is seen */ + void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope = false); + + /** if variable is allocated then there is a leak */ + void leakIfAllocated(const Token *vartok, const VarInfo &varInfo); + + void leakError(const Token* tok, const std::string &varname, int type) const; + void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname) const; + void deallocUseError(const Token *tok, const std::string &varname) const; + void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname); + void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type); + + /** message: user configuration is needed to complete analysis */ + void configurationInfo(const Token* tok, const std::pair& functionUsage); + }; +} + static bool isAutoDeallocType(const Type* type) { if (!type) return true; @@ -148,35 +257,44 @@ void VarInfo::possibleUsageAll(const std::pair& functionUsa } -void CheckLeakAutoVar::leakError(const Token *tok, const std::string &varname, int type) const +void CheckLeakAutoVarImpl::leakError(const Token *tok, const std::string &varname, int type) const { + // TODO + /* const CheckMemoryLeak checkmemleak(mTokenizer, mErrorLogger, mSettings); if (Library::isresource(type)) checkmemleak.resourceLeakError(tok, varname); else checkmemleak.memleakError(tok, varname); + */ } -void CheckLeakAutoVar::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const +void CheckLeakAutoVarImpl::mismatchError(const Token *deallocTok, const Token *allocTok, const std::string &varname) const { + // TODO + /* const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); const std::list callstack = { allocTok, deallocTok }; c.mismatchAllocDealloc(callstack, varname); + */ } -void CheckLeakAutoVar::deallocUseError(const Token *tok, const std::string &varname) const +void CheckLeakAutoVarImpl::deallocUseError(const Token *tok, const std::string &varname) const { + // TODO + /* const CheckMemoryLeak c(mTokenizer, mErrorLogger, mSettings); c.deallocuseError(tok, varname); + */ } -void CheckLeakAutoVar::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname) +void CheckLeakAutoVarImpl::deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname) { const std::list locations = { deallocTok, tok }; reportError(locations, Severity::error, "deallocret", "$symbol:" + varname + "\nReturning/dereferencing '$symbol' after it is deallocated / released", CWE672, Certainty::normal); } -void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair& functionUsage) +void CheckLeakAutoVarImpl::configurationInfo(const Token* tok, const std::pair& functionUsage) { if (mSettings->checkLibrary && functionUsage.second == VarInfo::USED && (!functionUsage.first || !functionUsage.first->function() || !functionUsage.first->function()->hasBody())) { @@ -188,7 +306,7 @@ void CheckLeakAutoVar::configurationInfo(const Token* tok, const std::pair locations = { prevFreeTok, tok }; @@ -199,7 +317,7 @@ void CheckLeakAutoVar::doubleFreeError(const Token *tok, const Token *prevFreeTo } -void CheckLeakAutoVar::check() +void CheckLeakAutoVarImpl::check() { if (mSettings->clang) return; @@ -297,7 +415,7 @@ static const Token * isFunctionCall(const Token * nameToken) return nullptr; } -bool CheckLeakAutoVar::checkScope(const Token * const startToken, +bool CheckLeakAutoVarImpl::checkScope(const Token * const startToken, VarInfo &varInfo, std::set notzero, nonneg int recursiveCount) @@ -804,7 +922,7 @@ bool CheckLeakAutoVar::checkScope(const Token * const startToken, } -const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo) +const Token * CheckLeakAutoVarImpl::checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo) { // Deallocation and then dereferencing pointer.. if (tok->varId() > 0) { @@ -869,7 +987,7 @@ const Token * CheckLeakAutoVar::checkTokenInsideExpression(const Token * const t } -void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const +void CheckLeakAutoVarImpl::changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const { const Library::AllocFunc* f = mSettings->library.getReallocFuncInfo(fTok); if (f && f->arg == -1 && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(fTok)) { @@ -890,7 +1008,7 @@ void CheckLeakAutoVar::changeAllocStatusIfRealloc(std::map &alloctype = varInfo.alloctype; const std::map::iterator var = alloctype.find(arg->varId()); @@ -919,7 +1037,7 @@ void CheckLeakAutoVar::changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocI } } -void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) +void CheckLeakAutoVarImpl::functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af) { // Ignore function call? if (mSettings->library.isLeakIgnore(mSettings->library.getFunctionName(tokName))) @@ -1038,7 +1156,7 @@ void CheckLeakAutoVar::functionCall(const Token *tokName, const Token *tokOpenin } -void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, +void CheckLeakAutoVarImpl::leakIfAllocated(const Token *vartok, const VarInfo &varInfo) { const std::map &alloctype = varInfo.alloctype; @@ -1055,7 +1173,7 @@ void CheckLeakAutoVar::leakIfAllocated(const Token *vartok, } } -void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) +void CheckLeakAutoVarImpl::ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope) { const std::map &alloctype = varInfo.alloctype; const auto& possibleUsage = varInfo.possibleUsage; @@ -1143,3 +1261,15 @@ void CheckLeakAutoVar::ret(const Token *tok, VarInfo &varInfo, const bool isEndO for (const int varId : toRemove) varInfo.erase(varId); } + +void CheckLeakAutoVar::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckLeakAutoVarImpl checkLeakAutoVar(&tokenizer, tokenizer.getSettings(), errorLogger); + checkLeakAutoVar.check(); +} + +void CheckLeakAutoVar::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckLeakAutoVarImpl c(nullptr, settings, errorLogger); + c.deallocReturnError(nullptr, nullptr, "p"); + c.configurationInfo(nullptr, { nullptr, VarInfo::USED }); // user configuration is needed to complete analysis + c.doubleFreeError(nullptr, nullptr, "varname", 0); +} diff --git a/lib/checkleakautovar.h b/lib/checkleakautovar.h index 23af88ff11c..7b559041b48 100644 --- a/lib/checkleakautovar.h +++ b/lib/checkleakautovar.h @@ -24,80 +24,12 @@ #include "check.h" #include "config.h" -#include "library.h" -#include "tokenize.h" -#include -#include #include -#include class ErrorLogger; class Settings; -class Token; - - -class CPPCHECKLIB VarInfo { -public: - enum AllocStatus { REALLOC = -3, OWNED = -2, DEALLOC = -1, NOALLOC = 0, ALLOC = 1 }; - struct AllocInfo { - AllocStatus status; - /** Allocation type. If it is a positive value then it corresponds to - * a Library allocation id. A negative value is a builtin - * checkleakautovar allocation type. - */ - int type; - int reallocedFromType = -1; - const Token * allocTok; - explicit AllocInfo(int type_ = 0, AllocStatus status_ = NOALLOC, const Token* allocTok_ = nullptr) : status(status_), type(type_), allocTok(allocTok_) {} - - bool managed() const { - return status < 0; - } - }; - enum Usage { USED, NORET }; - std::map alloctype; - std::map> possibleUsage; - std::set conditionalAlloc; - std::set referenced; - - void clear() { - alloctype.clear(); - possibleUsage.clear(); - conditionalAlloc.clear(); - referenced.clear(); - } - - void erase(nonneg int varid) { - alloctype.erase(varid); - possibleUsage.erase(varid); - conditionalAlloc.erase(varid); - referenced.erase(varid); - } - - void swap(VarInfo &other) { - alloctype.swap(other.alloctype); - possibleUsage.swap(other.possibleUsage); - conditionalAlloc.swap(other.conditionalAlloc); - referenced.swap(other.referenced); - } - - void reallocToAlloc(nonneg int varid) { - const AllocInfo& alloc = alloctype[varid]; - if (alloc.reallocedFromType >= 0) { - const std::map::iterator it = alloctype.find(alloc.reallocedFromType); - if (it != alloctype.end() && it->second.status == REALLOC) { - it->second.status = ALLOC; - } - } - } - - /** set possible usage for all variables */ - void possibleUsageAll(const std::pair& functionUsage); - - void print(); -}; - +class Tokenizer; /// @addtogroup Checks /// @{ @@ -109,68 +41,12 @@ class CPPCHECKLIB VarInfo { class CPPCHECKLIB CheckLeakAutoVar : public Check { public: /** This constructor is used when registering the CheckLeakAutoVar */ - CheckLeakAutoVar() : Check(myName()) {} + CheckLeakAutoVar() : Check("Leaks (auto variables)") {} private: - /** This constructor is used when running checks. */ - CheckLeakAutoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckLeakAutoVar checkLeakAutoVar(&tokenizer, tokenizer.getSettings(), errorLogger); - checkLeakAutoVar.check(); - } - - /** check for leaks in all scopes */ - void check(); - - /** check for leaks in a function scope */ - bool checkScope(const Token * const startToken, - VarInfo &varInfo, - std::set notzero, - nonneg int recursiveCount); - - /** Check token inside expression. - * @param tok token inside expression. - * @param varInfo Variable info - * @return next token to process (if no other checks needed for this token). NULL if other checks could be performed. - */ - const Token * checkTokenInsideExpression(const Token * const tok, VarInfo &varInfo); - - /** parse function call */ - void functionCall(const Token *tokName, const Token *tokOpeningPar, VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Library::AllocFunc* af); - - /** parse changes in allocation status */ - void changeAllocStatus(VarInfo &varInfo, const VarInfo::AllocInfo& allocation, const Token* tok, const Token* arg); - - /** update allocation status if reallocation function */ - void changeAllocStatusIfRealloc(std::map &alloctype, const Token *fTok, const Token *retTok) const; - - /** return. either "return" or end of variable scope is seen */ - void ret(const Token *tok, VarInfo &varInfo, const bool isEndOfScope = false); - - /** if variable is allocated then there is a leak */ - void leakIfAllocated(const Token *vartok, const VarInfo &varInfo); - - void leakError(const Token* tok, const std::string &varname, int type) const; - void mismatchError(const Token* deallocTok, const Token* allocTok, const std::string &varname) const; - void deallocUseError(const Token *tok, const std::string &varname) const; - void deallocReturnError(const Token *tok, const Token *deallocTok, const std::string &varname); - void doubleFreeError(const Token *tok, const Token *prevFreeTok, const std::string &varname, int type); - - /** message: user configuration is needed to complete analysis */ - void configurationInfo(const Token* tok, const std::pair& functionUsage); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckLeakAutoVar c(nullptr, settings, errorLogger); - c.deallocReturnError(nullptr, nullptr, "p"); - c.configurationInfo(nullptr, { nullptr, VarInfo::USED }); // user configuration is needed to complete analysis - c.doubleFreeError(nullptr, nullptr, "varname", 0); - } - - static std::string myName() { - return "Leaks (auto variables)"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Detect when a auto variable is allocated but not deallocated or deallocated twice.\n"; diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index a3c7d4a0177..35ba0e23722 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -20,6 +20,7 @@ #include "checkmemoryleak.h" #include "astutils.h" +#include "checkimpl.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" @@ -52,7 +53,96 @@ static const CWE CWE772(772U); // Missing Release of Resource after Effective L //--------------------------------------------------------------------------- -CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack) const +// Register this check class (by creating a static instance of it) +namespace { + /** @brief Base class for memory leaks checking */ + class CheckMemoryLeakImpl : public CheckImpl { + private: + /** + * Report error. Similar with the function Check::reportError + * @param tok the token where the error occurs + * @param severity the severity of the bug + * @param id type of message + * @param msg text + * @param cwe cwe number + */ + void reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; + + /** + * Report error. Similar with the function Check::reportError + * @param callstack callstack of error + * @param severity the severity of the bug + * @param id type of message + * @param msg text + * @param cwe cwe number + */ + void reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; + + public: + CheckMemoryLeakImpl() = delete; + CheckMemoryLeakImpl(const CheckMemoryLeakImpl &) = delete; + void operator=(const CheckMemoryLeakImpl &) = delete; + + CheckMemoryLeakImpl(const Tokenizer *t, const Settings *s, ErrorLogger *e) + : CheckImpl(t, s, e) {} + + /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ + enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many }; + + void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const; + + /** + * @brief Get type of deallocation at given position + * @param tok position + * @param varid variable id + * @return type of deallocation + */ + AllocType getDeallocationType(const Token *tok, nonneg int varid) const; + + /** + * @brief Get type of allocation at given position + */ + AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack = nullptr) const; + + /** + * @brief Get type of reallocation at given position + */ + AllocType getReallocationType(const Token *tok2, nonneg int varid) const; + + /** + * Check if token reopens a standard stream + * @param tok token to check + */ + bool isReopenStandardStream(const Token *tok) const; + /** + * Check if token opens /dev/null + * @param tok token to check + */ + bool isOpenDevNull(const Token *tok) const; + /** + * Report that there is a memory leak (new/malloc/etc) + * @param tok token where memory is leaked + * @param varname name of variable + */ + void memleakError(const Token *tok, const std::string &varname) const; + + /** + * Report that there is a resource leak (fopen/popen/etc) + * @param tok token where resource is leaked + * @param varname name of variable + */ + void resourceLeakError(const Token *tok, const std::string &varname) const; + + void deallocuseError(const Token *tok, const std::string &varname) const; + void mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const; + void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const; + + /** What type of allocated memory does the given function return? */ + AllocType functionReturnType(const Function* func, std::list *callstack = nullptr) const; + }; +} + +CheckMemoryLeakImpl::AllocType CheckMemoryLeakImpl::getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack) const { // What we may have... // * var = (char *)malloc(10); @@ -76,7 +166,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, if (reallocType != No) return reallocType; - if (mTokenizer_->isCPP() && tok2->str() == "new") { + if (mTokenizer->isCPP() && tok2->str() == "new") { if (tok2->strAt(1) == "(" && !Token::Match(tok2->next(),"( std| ::| nothrow )")) return No; if (tok2->astOperand1() && (tok2->astOperand1()->str() == "[" || (tok2->astOperand1()->astOperand1() && tok2->astOperand1()->astOperand1()->str() == "["))) @@ -95,7 +185,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, return New; } - if (mSettings_->hasLib("posix")) { + if (mSettings->hasLib("posix")) { if (Token::Match(tok2, "open|openat|creat|mkstemp|mkostemp|socket (")) { // simple sanity check of function parameters.. // TODO: Make such check for all these functions @@ -114,11 +204,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, } // Does tok2 point on a Library allocation function? - const int alloctype = mSettings_->library.getAllocId(tok2, -1); + const int alloctype = mSettings->library.getAllocId(tok2, -1); if (alloctype > 0) { - if (alloctype == mSettings_->library.deallocId("free")) + if (alloctype == mSettings->library.deallocId("free")) return Malloc; - if (alloctype == mSettings_->library.deallocId("fclose")) + if (alloctype == mSettings->library.deallocId("fclose")) return File; return Library::ismemory(alloctype) ? OtherMem : OtherRes; } @@ -145,7 +235,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getAllocationType(const Token *tok2, } -CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok2, nonneg int varid) const +CheckMemoryLeakImpl::AllocType CheckMemoryLeakImpl::getReallocationType(const Token *tok2, nonneg int varid) const { // What we may have... // * var = (char *)realloc(..; @@ -159,7 +249,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok if (!Token::Match(tok2, "%name% (")) return No; - const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok2); + const Library::AllocFunc *f = mSettings->library.getReallocFuncInfo(tok2); if (!(f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok2))) return No; const auto args = getArguments(tok2); @@ -173,11 +263,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok if (varid > 0 && !Token::Match(arg, "%varid% [,)]", varid)) return No; - const int realloctype = mSettings_->library.getReallocId(tok2, -1); + const int realloctype = mSettings->library.getReallocId(tok2, -1); if (realloctype > 0) { - if (realloctype == mSettings_->library.deallocId("free")) + if (realloctype == mSettings->library.deallocId("free")) return Malloc; - if (realloctype == mSettings_->library.deallocId("fclose")) + if (realloctype == mSettings->library.deallocId("fclose")) return File; return Library::ismemory(realloctype) ? OtherMem : OtherRes; } @@ -185,9 +275,9 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getReallocationType(const Token *tok } -CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok, nonneg int varid) const +CheckMemoryLeakImpl::AllocType CheckMemoryLeakImpl::getDeallocationType(const Token *tok, nonneg int varid) const { - if (mTokenizer_->isCPP() && tok->str() == "delete" && tok->astOperand1()) { + if (mTokenizer->isCPP() && tok->str() == "delete" && tok->astOperand1()) { const Token* vartok = tok->astOperand1(); if (Token::Match(vartok, ".|::")) vartok = vartok->astOperand2(); @@ -216,7 +306,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok if (tok->str() == "realloc" && Token::simpleMatch(vartok->next(), ", 0 )")) return Malloc; - if (mSettings_->hasLib("posix")) { + if (mSettings->hasLib("posix")) { if (tok->str() == "close") return Fd; if (tok->str() == "pclose") @@ -224,11 +314,11 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok } // Does tok point on a Library deallocation function? - const int dealloctype = mSettings_->library.getDeallocId(tok, argNr); + const int dealloctype = mSettings->library.getDeallocId(tok, argNr); if (dealloctype > 0) { - if (dealloctype == mSettings_->library.deallocId("free")) + if (dealloctype == mSettings->library.deallocId("free")) return Malloc; - if (dealloctype == mSettings_->library.deallocId("fclose")) + if (dealloctype == mSettings->library.deallocId("fclose")) return File; return Library::ismemory(dealloctype) ? OtherMem : OtherRes; } @@ -240,10 +330,10 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::getDeallocationType(const Token *tok return No; } -bool CheckMemoryLeak::isReopenStandardStream(const Token *tok) const +bool CheckMemoryLeakImpl::isReopenStandardStream(const Token *tok) const { if (getReallocationType(tok, 0) == File) { - const Library::AllocFunc *f = mSettings_->library.getReallocFuncInfo(tok); + const Library::AllocFunc *f = mSettings->library.getReallocFuncInfo(tok); if (f && f->reallocArg > 0 && f->reallocArg <= numberOfArguments(tok)) { const Token* arg = getArguments(tok).at(f->reallocArg - 1); if (Token::Match(arg, "stdin|stdout|stderr")) @@ -253,9 +343,9 @@ bool CheckMemoryLeak::isReopenStandardStream(const Token *tok) const return false; } -bool CheckMemoryLeak::isOpenDevNull(const Token *tok) const +bool CheckMemoryLeakImpl::isOpenDevNull(const Token *tok) const { - if (mSettings_->hasLib("posix") && tok->str() == "open" && numberOfArguments(tok) == 2) { + if (mSettings->hasLib("posix") && tok->str() == "open" && numberOfArguments(tok) == 2) { const Token* arg = getArguments(tok).at(0); if (Token::simpleMatch(arg, "\"/dev/null\"")) return true; @@ -263,24 +353,18 @@ bool CheckMemoryLeak::isOpenDevNull(const Token *tok) const return false; } -//-------------------------------------------------------------------------- - - -//-------------------------------------------------------------------------- - -void CheckMemoryLeak::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const +void CheckMemoryLeakImpl::memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const { - if (alloctype == CheckMemoryLeak::File || - alloctype == CheckMemoryLeak::Pipe || - alloctype == CheckMemoryLeak::Fd || - alloctype == CheckMemoryLeak::OtherRes) + if (alloctype == File || + alloctype == Pipe || + alloctype == Fd || + alloctype == OtherRes) resourceLeakError(tok, varname); else memleakError(tok, varname); } -//--------------------------------------------------------------------------- -void CheckMemoryLeak::reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const +void CheckMemoryLeakImpl::reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const { std::list callstack; @@ -290,26 +374,26 @@ void CheckMemoryLeak::reportErr(const Token *tok, Severity severity, const std:: reportErr(callstack, severity, id, msg, cwe); } -void CheckMemoryLeak::reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const +void CheckMemoryLeakImpl::reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const { - const ErrorMessage errmsg(callstack, mTokenizer_ ? &mTokenizer_->list : nullptr, severity, id, msg, cwe, Certainty::normal); - if (mErrorLogger_) - mErrorLogger_->reportErr(errmsg); + const ErrorMessage errmsg(callstack, mTokenizer ? &mTokenizer->list : nullptr, severity, id, msg, cwe, Certainty::normal); + if (mErrorLogger) + mErrorLogger->reportErr(errmsg); else Check::writeToErrorList(errmsg); } -void CheckMemoryLeak::memleakError(const Token *tok, const std::string &varname) const +void CheckMemoryLeakImpl::memleakError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "memleak", "$symbol:" + varname + "\nMemory leak: $symbol", CWE(401U)); } -void CheckMemoryLeak::memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const +void CheckMemoryLeakImpl::memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const { reportErr(tok, Severity::error, "memleakOnRealloc", "$symbol:" + varname + "\nCommon " + reallocfunction + " mistake: \'$symbol\' nulled but not freed upon failure", CWE(401U)); } -void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &varname) const +void CheckMemoryLeakImpl::resourceLeakError(const Token *tok, const std::string &varname) const { std::string errmsg("Resource leak"); if (!varname.empty()) @@ -317,17 +401,17 @@ void CheckMemoryLeak::resourceLeakError(const Token *tok, const std::string &var reportErr(tok, Severity::error, "resourceLeak", errmsg, CWE(775U)); } -void CheckMemoryLeak::deallocuseError(const Token *tok, const std::string &varname) const +void CheckMemoryLeakImpl::deallocuseError(const Token *tok, const std::string &varname) const { reportErr(tok, Severity::error, "deallocuse", "$symbol:" + varname + "\nDereferencing '$symbol' after it is deallocated / released", CWE(416U)); } -void CheckMemoryLeak::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const +void CheckMemoryLeakImpl::mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const { reportErr(callstack, Severity::error, "mismatchAllocDealloc", "$symbol:" + varname + "\nMismatching allocation and deallocation: $symbol", CWE(762U)); } -CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* func, std::list *callstack) const +CheckMemoryLeakImpl::AllocType CheckMemoryLeakImpl::functionReturnType(const Function* func, std::list *callstack) const { if (!func || !func->hasBody() || !func->functionScope) return No; @@ -375,7 +459,7 @@ CheckMemoryLeak::AllocType CheckMemoryLeak::functionReturnType(const Function* f if (Token::Match(tok, "= %varid% ;", varid)) { return No; } - if (!mTokenizer_->isC() && Token::Match(tok, "[(,] %varid% [,)]", varid)) { + if (!mTokenizer->isC() && Token::Match(tok, "[(,] %varid% [,)]", varid)) { return No; } if (Token::Match(tok, "[(,] & %varid% [.,)]", varid)) { @@ -418,6 +502,21 @@ static bool ifvar(const Token *tok, nonneg int varid, const std::string &comp, c return (vartok && vartok->varId() == varid); } +namespace { + class CheckMemoryLeakInFunctionImpl : public CheckMemoryLeakImpl { + public: + /** @brief This constructor is used when running checks */ + CheckMemoryLeakInFunctionImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckMemoryLeakImpl(tokenizer, settings, errorLogger) {} + + /** + * Checking for a memory leak caused by improper realloc usage. + */ + void checkReallocUsage(); + }; +} + + //--------------------------------------------------------------------------- // Check for memory leaks due to improper realloc() usage. // Below, "a" may be set to null without being freed if realloc() cannot @@ -425,7 +524,7 @@ static bool ifvar(const Token *tok, nonneg int varid, const std::string &comp, c // a = malloc(10); a = realloc(a, 100); //--------------------------------------------------------------------------- -void CheckMemoryLeakInFunction::checkReallocUsage() +void CheckMemoryLeakInFunctionImpl::checkReallocUsage() { logChecker("CheckMemoryLeakInFunction::checkReallocUsage"); @@ -493,6 +592,23 @@ void CheckMemoryLeakInFunction::checkReallocUsage() } } } + +void CheckMemoryLeakInFunction::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckMemoryLeakInFunctionImpl checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); + checkMemoryLeak.checkReallocUsage(); +} + +/** Report all possible errors (for the --errorlist) */ +void CheckMemoryLeakInFunction::getErrorMessages(ErrorLogger *e, const Settings *settings) const { + CheckMemoryLeakInFunctionImpl c(nullptr, settings, e); + c.memleakError(nullptr, "varname"); + c.resourceLeakError(nullptr, "varname"); + c.deallocuseError(nullptr, "varname"); + const std::list callstack; + c.mismatchAllocDealloc(callstack, "varname"); + c.memleakUponReallocFailureError(nullptr, "realloc", "varname"); +} + //--------------------------------------------------------------------------- @@ -500,8 +616,26 @@ void CheckMemoryLeakInFunction::checkReallocUsage() // Checks for memory leaks in classes.. //--------------------------------------------------------------------------- +namespace { + class CheckMemoryLeakInClassImpl : public CheckMemoryLeakImpl { + public: + CheckMemoryLeakInClassImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckMemoryLeakImpl(tokenizer, settings, errorLogger) {} + + void check(); -void CheckMemoryLeakInClass::check() + void variable(const Scope *scope, const Token *tokVarname); + + /** Public functions: possible double-allocation */ + void checkPublicFunctions(const Scope *scope, const Token *classtok); + void publicAllocationError(const Token *tok, const std::string &varname); + + void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname); + }; +} + + +void CheckMemoryLeakInClassImpl::check() { logChecker("CheckMemoryLeakInClass::check"); @@ -526,15 +660,15 @@ void CheckMemoryLeakInClass::check() } -void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarname) +void CheckMemoryLeakInClassImpl::variable(const Scope *scope, const Token *tokVarname) { const std::string& varname = tokVarname->str(); const int varid = tokVarname->varId(); const std::string& classname = scope->className; // Check if member variable has been allocated and deallocated.. - CheckMemoryLeak::AllocType memberAlloc = CheckMemoryLeak::No; - CheckMemoryLeak::AllocType memberDealloc = CheckMemoryLeak::No; + AllocType memberAlloc = AllocType::No; + AllocType memberDealloc = AllocType::No; bool allocInConstructor = false; bool deallocInDestructor = false; @@ -546,7 +680,7 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam if (!func.hasBody()) { if (destructor && !func.isDefault()) { // implementation for destructor is not seen and not defaulted => assume it deallocates all variables properly deallocInDestructor = true; - memberDealloc = CheckMemoryLeak::Many; + memberDealloc = AllocType::Many; } continue; } @@ -579,14 +713,14 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam allocTok = tok->astParent()->astParent()->astOperand2(); AllocType alloc = getAllocationType(allocTok, 0); - if (alloc != CheckMemoryLeak::No) { + if (alloc != AllocType::No) { if (constructor) allocInConstructor = true; - if (memberAlloc != No && memberAlloc != alloc) - alloc = CheckMemoryLeak::Many; + if (memberAlloc != AllocType::No && memberAlloc != alloc) + alloc = AllocType::Many; - if (alloc != CheckMemoryLeak::Many && memberDealloc != CheckMemoryLeak::No && memberDealloc != CheckMemoryLeak::Many && memberDealloc != alloc) { + if (alloc != AllocType::Many && memberDealloc != AllocType::No && memberDealloc != AllocType::Many && memberDealloc != alloc) { mismatchAllocDealloc({tok}, classname + "::" + varname); } @@ -602,18 +736,18 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam // some usage in the destructor => assume it's related // to deallocation if (destructor && tok->str() == varname) - dealloc = CheckMemoryLeak::Many; - if (dealloc != CheckMemoryLeak::No) { + dealloc = AllocType::Many; + if (dealloc != AllocType::No) { if (destructor) deallocInDestructor = true; - if (dealloc != CheckMemoryLeak::Many && memberAlloc != CheckMemoryLeak::No && memberAlloc != Many && memberAlloc != dealloc) { + if (dealloc != AllocType::Many && memberAlloc != AllocType::No && memberAlloc != Many && memberAlloc != dealloc) { mismatchAllocDealloc({tok}, classname + "::" + varname); } // several types of allocation/deallocation? - if (memberDealloc != CheckMemoryLeak::No && memberDealloc != dealloc) - dealloc = CheckMemoryLeak::Many; + if (memberDealloc != AllocType::No && memberDealloc != dealloc) + dealloc = AllocType::Many; memberDealloc = dealloc; } @@ -628,12 +762,12 @@ void CheckMemoryLeakInClass::variable(const Scope *scope, const Token *tokVarnam if (allocInConstructor && !deallocInDestructor) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); - } else if (memberAlloc != CheckMemoryLeak::No && memberDealloc == CheckMemoryLeak::No) { + } else if (memberAlloc != AllocType::No && memberDealloc == AllocType::No) { unsafeClassError(tokVarname, classname, classname + "::" + varname /*, memberAlloc*/); } } -void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) +void CheckMemoryLeakInClassImpl::unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname) { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -646,7 +780,7 @@ void CheckMemoryLeakInClass::unsafeClassError(const Token *tok, const std::strin } -void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Token *classtok) +void CheckMemoryLeakInClassImpl::checkPublicFunctions(const Scope *scope, const Token *classtok) { // Check that public functions deallocate the pointers that they allocate. // There is no checking how these functions are used and therefore it @@ -663,26 +797,54 @@ void CheckMemoryLeakInClass::checkPublicFunctions(const Scope *scope, const Toke func.access == AccessControl::Public && func.hasBody()) { const Token *tok2 = func.functionScope->bodyStart->next(); if (Token::Match(tok2, "%varid% =", varid)) { - const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(2), varid); - if (alloc != CheckMemoryLeak::No) + const AllocType alloc = getAllocationType(tok2->tokAt(2), varid); + if (alloc != AllocType::No) publicAllocationError(tok2, tok2->str()); } else if (Token::Match(tok2, "%type% :: %varid% =", varid) && tok2->str() == scope->className) { - const CheckMemoryLeak::AllocType alloc = getAllocationType(tok2->tokAt(4), varid); - if (alloc != CheckMemoryLeak::No) + const AllocType alloc = getAllocationType(tok2->tokAt(4), varid); + if (alloc != AllocType::No) publicAllocationError(tok2, tok2->strAt(2)); } } } } -void CheckMemoryLeakInClass::publicAllocationError(const Token *tok, const std::string &varname) +void CheckMemoryLeakInClassImpl::publicAllocationError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "publicAllocationError", "$symbol:" + varname + "\nPossible leak in public function. The pointer '$symbol' is not deallocated before it is allocated.", CWE398, Certainty::normal); } +void CheckMemoryLeakInClass::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + if (!tokenizer.isCPP()) + return; + + CheckMemoryLeakInClassImpl checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); +} + +void CheckMemoryLeakInClass::getErrorMessages(ErrorLogger *e, const Settings *settings) const { + CheckMemoryLeakInClassImpl c(nullptr, settings, e); + c.publicAllocationError(nullptr, "varname"); + c.unsafeClassError(nullptr, "class", "class::varname"); +} + +namespace { + class CheckMemoryLeakStructMemberImpl : public CheckMemoryLeakImpl { + public: + CheckMemoryLeakStructMemberImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckMemoryLeakImpl(tokenizer, settings, errorLogger) {} + + void check(); + + /** Is local variable allocated with malloc? */ + bool isMalloc(const Variable *variable) const; -void CheckMemoryLeakStructMember::check() + void checkStructVariable(const Variable* const variable) const; + }; +} + +void CheckMemoryLeakStructMemberImpl::check() { if (mSettings->clang) return; @@ -701,7 +863,7 @@ void CheckMemoryLeakStructMember::check() } } -bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) const +bool CheckMemoryLeakStructMemberImpl::isMalloc(const Variable *variable) const { if (!variable) return false; @@ -716,7 +878,7 @@ bool CheckMemoryLeakStructMember::isMalloc(const Variable *variable) const return alloc; } -void CheckMemoryLeakStructMember::checkStructVariable(const Variable* const variable) const +void CheckMemoryLeakStructMemberImpl::checkStructVariable(const Variable* const variable) const { if (!variable) return; @@ -927,9 +1089,45 @@ void CheckMemoryLeakStructMember::checkStructVariable(const Variable* const vari } } +void CheckMemoryLeakStructMember::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckMemoryLeakStructMemberImpl checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); +} +namespace { + class CheckMemoryLeakNoVarImpl : public CheckMemoryLeakImpl { + public: + CheckMemoryLeakNoVarImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckMemoryLeakImpl(tokenizer, settings, errorLogger) {} + + void check(); + + /** + * @brief %Check if an input argument to a function is the return value of an allocation function + * like malloc(), and the function does not release it. + * @param scope The scope of the function to check. + */ + void checkForUnreleasedInputArgument(const Scope *scope); + + /** + * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned. + * @param scope The scope of the function to check. + */ + void checkForUnusedReturnValue(const Scope *scope); + + /** + * @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr. + * @param scope The scope of the function to check. + */ + void checkForUnsafeArgAlloc(const Scope *scope); + + void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); + void returnValueNotUsedError(const Token* tok, const std::string &alloc); + void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType); + }; +} -void CheckMemoryLeakNoVar::check() +void CheckMemoryLeakNoVarImpl::check() { logChecker("CheckMemoryLeakNoVar::check"); @@ -955,7 +1153,7 @@ void CheckMemoryLeakNoVar::check() // Checks if an input argument to a function is the return value of an allocation function // like malloc(), and the function does not release it. //--------------------------------------------------------------------------- -void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) +void CheckMemoryLeakNoVarImpl::checkForUnreleasedInputArgument(const Scope *scope) { // parse the executable scope until tok is reached... for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { @@ -1028,7 +1226,7 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope) //--------------------------------------------------------------------------- // Checks if a call to an allocation function like malloc() is made and its return value is not assigned. //--------------------------------------------------------------------------- -void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) +void CheckMemoryLeakNoVarImpl::checkForUnusedReturnValue(const Scope *scope) { for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { const bool isNew = mTokenizer->isCPP() && tok->str() == "new"; @@ -1090,7 +1288,7 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) // f(shared_ptr(new int(42)), g()); // } //--------------------------------------------------------------------------- -void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) +void CheckMemoryLeakNoVarImpl::checkForUnsafeArgAlloc(const Scope *scope) { // This test only applies to C++ source if (!mTokenizer->isCPP() || !mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) @@ -1135,17 +1333,17 @@ void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) } } -void CheckMemoryLeakNoVar::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall) +void CheckMemoryLeakNoVarImpl::functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall) { reportError(loc, Severity::error, "leakNoVarFunctionCall", "Allocation with " + alloc + ", " + functionCall + " doesn't release it.", CWE772, Certainty::normal); } -void CheckMemoryLeakNoVar::returnValueNotUsedError(const Token *tok, const std::string &alloc) +void CheckMemoryLeakNoVarImpl::returnValueNotUsedError(const Token *tok, const std::string &alloc) { reportError(tok, Severity::error, "leakReturnValNotUsed", "$symbol:" + alloc + "\nReturn value of allocation function '$symbol' is not stored.", CWE771, Certainty::normal); } -void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType) +void CheckMemoryLeakNoVarImpl::unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string& objType) { const std::string factoryFunc = ptrType == "shared_ptr" ? "make_shared" : "make_unique"; reportError(tok, Severity::warning, "leakUnsafeArgAlloc", @@ -1154,3 +1352,15 @@ void CheckMemoryLeakNoVar::unsafeArgAllocError(const Token *tok, const std::stri CWE401, Certainty::inconclusive); // Inconclusive because funcName may never throw } + +void CheckMemoryLeakNoVar::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckMemoryLeakNoVarImpl checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); + checkMemoryLeak.check(); +} + +void CheckMemoryLeakNoVar::getErrorMessages(ErrorLogger *e, const Settings *settings) const { + CheckMemoryLeakNoVarImpl c(nullptr, settings, e); + c.functionCallLeak(nullptr, "funcName", "funcName"); + c.returnValueNotUsedError(nullptr, "funcName"); + c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int"); +} diff --git a/lib/checkmemoryleak.h b/lib/checkmemoryleak.h index 20309873c14..4b007e264be 100644 --- a/lib/checkmemoryleak.h +++ b/lib/checkmemoryleak.h @@ -34,121 +34,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" -#include #include -class Function; -class Scope; class Settings; -class Token; -class Variable; class ErrorLogger; -struct CWE; -enum class Severity; - -/// @addtogroup Core -/// @{ - -/** @brief Base class for memory leaks checking */ -class CPPCHECKLIB CheckMemoryLeak { -private: - /** For access to the tokens */ - const Tokenizer * const mTokenizer_; - - /** ErrorLogger used to report errors */ - ErrorLogger * const mErrorLogger_; - - /** Enabled standards */ - const Settings * const mSettings_; - - /** - * Report error. Similar with the function Check::reportError - * @param tok the token where the error occurs - * @param severity the severity of the bug - * @param id type of message - * @param msg text - * @param cwe cwe number - */ - void reportErr(const Token *tok, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; - - /** - * Report error. Similar with the function Check::reportError - * @param callstack callstack of error - * @param severity the severity of the bug - * @param id type of message - * @param msg text - * @param cwe cwe number - */ - void reportErr(const std::list &callstack, Severity severity, const std::string &id, const std::string &msg, const CWE &cwe) const; - -public: - CheckMemoryLeak() = delete; - CheckMemoryLeak(const CheckMemoryLeak &) = delete; - void operator=(const CheckMemoryLeak &) = delete; - - CheckMemoryLeak(const Tokenizer *t, ErrorLogger *e, const Settings *s) - : mTokenizer_(t), mErrorLogger_(e), mSettings_(s) {} - - /** @brief What type of allocation are used.. the "Many" means that several types of allocation and deallocation are used */ - enum AllocType { No, Malloc, New, NewArray, File, Fd, Pipe, OtherMem, OtherRes, Many }; - - void memoryLeak(const Token *tok, const std::string &varname, AllocType alloctype) const; - - /** - * @brief Get type of deallocation at given position - * @param tok position - * @param varid variable id - * @return type of deallocation - */ - AllocType getDeallocationType(const Token *tok, nonneg int varid) const; - - /** - * @brief Get type of allocation at given position - */ - AllocType getAllocationType(const Token *tok2, nonneg int varid, std::list *callstack = nullptr) const; - - /** - * @brief Get type of reallocation at given position - */ - AllocType getReallocationType(const Token *tok2, nonneg int varid) const; - - /** - * Check if token reopens a standard stream - * @param tok token to check - */ - bool isReopenStandardStream(const Token *tok) const; - /** - * Check if token opens /dev/null - * @param tok token to check - */ - bool isOpenDevNull(const Token *tok) const; - /** - * Report that there is a memory leak (new/malloc/etc) - * @param tok token where memory is leaked - * @param varname name of variable - */ - void memleakError(const Token *tok, const std::string &varname) const; - - /** - * Report that there is a resource leak (fopen/popen/etc) - * @param tok token where resource is leaked - * @param varname name of variable - */ - void resourceLeakError(const Token *tok, const std::string &varname) const; - - void deallocuseError(const Token *tok, const std::string &varname) const; - void mismatchAllocDealloc(const std::list &callstack, const std::string &varname) const; - void memleakUponReallocFailureError(const Token *tok, const std::string &reallocfunction, const std::string &varname) const; - - /** What type of allocated memory does the given function return? */ - AllocType functionReturnType(const Function* func, std::list *callstack = nullptr) const; -}; - -/// @} - - +class Tokenizer; /// @addtogroup Checks /// @{ @@ -164,46 +55,18 @@ class CPPCHECKLIB CheckMemoryLeak { * -# finally, check if the simplified token list contain any leaks. */ -class CPPCHECKLIB CheckMemoryLeakInFunction : public Check, public CheckMemoryLeak { +class CPPCHECKLIB CheckMemoryLeakInFunction : public Check { friend class TestMemleakInFunction; public: /** @brief This constructor is used when registering this class */ - CheckMemoryLeakInFunction() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + CheckMemoryLeakInFunction() : Check("Memory leaks (function variables)") {} private: - /** @brief This constructor is used when running checks */ - CheckMemoryLeakInFunction(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckMemoryLeakInFunction checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); - checkMemoryLeak.checkReallocUsage(); - } - - /** - * Checking for a memory leak caused by improper realloc usage. - */ - void checkReallocUsage(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** Report all possible errors (for the --errorlist) */ - void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { - CheckMemoryLeakInFunction c(nullptr, settings, e); - c.memleakError(nullptr, "varname"); - c.resourceLeakError(nullptr, "varname"); - c.deallocuseError(nullptr, "varname"); - const std::list callstack; - c.mismatchAllocDealloc(callstack, "varname"); - c.memleakUponReallocFailureError(nullptr, "realloc", "varname"); - } - - /** - * Get name of class (--doc) - * @return name of class - */ - static std::string myName() { - return "Memory leaks (function variables)"; - } + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override; /** * Get class information (--doc) @@ -215,48 +78,20 @@ class CPPCHECKLIB CheckMemoryLeakInFunction : public Check, public CheckMemoryLe }; - /** * @brief %Check class variables, variables that are allocated in the constructor should be deallocated in the destructor */ -class CPPCHECKLIB CheckMemoryLeakInClass : public Check, private CheckMemoryLeak { +class CPPCHECKLIB CheckMemoryLeakInClass : public Check { friend class TestMemleakInClass; public: - CheckMemoryLeakInClass() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + CheckMemoryLeakInClass() : Check("Memory leaks (class variables)") {} private: - CheckMemoryLeakInClass(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (!tokenizer.isCPP()) - return; + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - CheckMemoryLeakInClass checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); - checkMemoryLeak.check(); - } - - void check(); - - void variable(const Scope *scope, const Token *tokVarname); - - /** Public functions: possible double-allocation */ - void checkPublicFunctions(const Scope *scope, const Token *classtok); - void publicAllocationError(const Token *tok, const std::string &varname); - - void unsafeClassError(const Token *tok, const std::string &classname, const std::string &varname); - - void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { - CheckMemoryLeakInClass c(nullptr, settings, e); - c.publicAllocationError(nullptr, "varname"); - c.unsafeClassError(nullptr, "class", "class::varname"); - } - - static std::string myName() { - return "Memory leaks (class variables)"; - } + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override; std::string classInfo() const override { return "If the constructor allocate memory then the destructor must deallocate it.\n"; @@ -267,34 +102,17 @@ class CPPCHECKLIB CheckMemoryLeakInClass : public Check, private CheckMemoryLeak /** @brief detect simple memory leaks for struct members */ -class CPPCHECKLIB CheckMemoryLeakStructMember : public Check, private CheckMemoryLeak { +class CPPCHECKLIB CheckMemoryLeakStructMember : public Check { friend class TestMemleakStructMember; public: - CheckMemoryLeakStructMember() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + CheckMemoryLeakStructMember() : Check("Memory leaks (struct members)") {} private: - CheckMemoryLeakStructMember(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckMemoryLeakStructMember checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); - checkMemoryLeak.check(); - } - - void check(); - - /** Is local variable allocated with malloc? */ - bool isMalloc(const Variable *variable) const; - - void checkStructVariable(const Variable* const variable) const; + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; void getErrorMessages(ErrorLogger * /*errorLogger*/, const Settings * /*settings*/) const override {} - static std::string myName() { - return "Memory leaks (struct members)"; - } - std::string classInfo() const override { return "Don't forget to deallocate struct members\n"; } @@ -304,56 +122,16 @@ class CPPCHECKLIB CheckMemoryLeakStructMember : public Check, private CheckMemor /** @brief detect simple memory leaks (address not taken) */ -class CPPCHECKLIB CheckMemoryLeakNoVar : public Check, private CheckMemoryLeak { +class CPPCHECKLIB CheckMemoryLeakNoVar : public Check { friend class TestMemleakNoVar; public: - CheckMemoryLeakNoVar() : Check(myName()), CheckMemoryLeak(nullptr, nullptr, nullptr) {} + CheckMemoryLeakNoVar() : Check("Memory leaks (address not taken)") {} private: - CheckMemoryLeakNoVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger), CheckMemoryLeak(tokenizer, errorLogger, settings) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckMemoryLeakNoVar checkMemoryLeak(&tokenizer, tokenizer.getSettings(), errorLogger); - checkMemoryLeak.check(); - } - - void check(); - - /** - * @brief %Check if an input argument to a function is the return value of an allocation function - * like malloc(), and the function does not release it. - * @param scope The scope of the function to check. - */ - void checkForUnreleasedInputArgument(const Scope *scope); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** - * @brief %Check if a call to an allocation function like malloc() is made and its return value is not assigned. - * @param scope The scope of the function to check. - */ - void checkForUnusedReturnValue(const Scope *scope); - - /** - * @brief %Check if an exception could cause a leak in an argument constructed with shared_ptr/unique_ptr. - * @param scope The scope of the function to check. - */ - void checkForUnsafeArgAlloc(const Scope *scope); - - void functionCallLeak(const Token *loc, const std::string &alloc, const std::string &functionCall); - void returnValueNotUsedError(const Token* tok, const std::string &alloc); - void unsafeArgAllocError(const Token *tok, const std::string &funcName, const std::string &ptrType, const std::string &objType); - - void getErrorMessages(ErrorLogger *e, const Settings *settings) const override { - CheckMemoryLeakNoVar c(nullptr, settings, e); - c.functionCallLeak(nullptr, "funcName", "funcName"); - c.returnValueNotUsedError(nullptr, "funcName"); - c.unsafeArgAllocError(nullptr, "funcName", "shared_ptr", "int"); - } - - static std::string myName() { - return "Memory leaks (address not taken)"; - } + void getErrorMessages(ErrorLogger *e, const Settings *settings) const override; std::string classInfo() const override { return "Not taking the address to allocated memory\n"; diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 86a6542f0b6..184ffad76ab 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -21,6 +21,7 @@ #include "checknullpointer.h" #include "astutils.h" +#include "checkimpl.h" #include "ctu.h" #include "errorlogger.h" #include "errortypes.h" @@ -40,17 +41,60 @@ //--------------------------------------------------------------------------- -// CWE ids used: -static const CWE CWE_NULL_POINTER_DEREFERENCE(476U); -static const CWE CWE_INCORRECT_CALCULATION(682U); - // Register this check class (by creating a static instance of it) namespace { CheckNullPointer instance; } +// CWE ids used: +static const CWE CWE_NULL_POINTER_DEREFERENCE(476U); +static const CWE CWE_INCORRECT_CALCULATION(682U); + //--------------------------------------------------------------------------- +namespace { + class CheckNullPointerImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckNullPointerImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** + * @brief parse a function call and extract information about variable usage + * @param tok first token + * @param var variables that the function read / write. + * @param library --library files data + */ + static void parseFunctionCall(const Token &tok, + std::list &var, + const Library *library); + + /** @brief possible null pointer dereference */ + void nullPointer(); + + /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ + void nullConstantDereference(); + + void nullPointerError(const Token *tok) { + ValueFlow::Value v(0); + v.setKnown(); + nullPointerError(tok, emptyString, &v, false); + } + void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive); + + /** + * @brief Does one part of the check for nullPointer(). + * Dereferencing a pointer and then checking if it's NULL.. + */ + void nullPointerByDeRefAndChec(); + + /** undefined null pointer arithmetic */ + void arithmetic(); + void pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); + void redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive); + }; +} + static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsigned int arg) { return !func || (func->argCount() >= arg && func->getArgumentVar(arg - 1) && func->getArgumentVar(arg - 1)->isPointer()); @@ -62,7 +106,7 @@ static bool checkNullpointerFunctionCallPlausibility(const Function* func, unsig * @param var variables that the function read / write. * @param library --library files data */ -void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library *library) +void CheckNullPointerImpl::parseFunctionCall(const Token &tok, std::list &var, const Library *library) { if (Token::Match(&tok, "%name% ( )") || !tok.tokAt(2)) return; @@ -146,11 +190,6 @@ namespace { * @param unknown it is not known if there is a pointer dereference (could be reported as a debug message) * @return true => there is a dereference */ -bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown) const -{ - return isPointerDeRef(tok, unknown, mSettings); -} - bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings) { unknown = false; @@ -165,7 +204,7 @@ bool CheckNullPointer::isPointerDeRef(const Token *tok, bool &unknown, const Set } if (ftok && ftok->previous()) { std::list varlist; - parseFunctionCall(*ftok->previous(), varlist, &settings->library); + CheckNullPointerImpl::parseFunctionCall(*ftok->previous(), varlist, &settings->library); if (std::find(varlist.cbegin(), varlist.cend(), tok) != varlist.cend()) { return true; } @@ -281,7 +320,7 @@ static bool isNullablePointer(const Token* tok, const Settings* settings) return false; } -void CheckNullPointer::nullPointerByDeRefAndChec() +void CheckNullPointerImpl::nullPointerByDeRefAndChec() { const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); @@ -308,7 +347,7 @@ void CheckNullPointer::nullPointerByDeRefAndChec() // Pointer dereference. bool unknown = false; - if (!isPointerDeRef(tok,unknown)) { + if (!CheckNullPointer::isPointerDeRef(tok,unknown,mSettings)) { if (unknown) nullPointerError(tok, tok->expressionString(), value, true); continue; @@ -318,7 +357,7 @@ void CheckNullPointer::nullPointerByDeRefAndChec() } } -void CheckNullPointer::nullPointer() +void CheckNullPointerImpl::nullPointer() { logChecker("CheckNullPointer::nullPointer"); nullPointerByDeRefAndChec(); @@ -332,7 +371,7 @@ namespace { } /** Dereferencing null constant (simplified token list) */ -void CheckNullPointer::nullConstantDereference() +void CheckNullPointerImpl::nullConstantDereference() { logChecker("CheckNullPointer::nullConstantDereference"); @@ -426,7 +465,7 @@ void CheckNullPointer::nullConstantDereference() } } -void CheckNullPointer::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) +void CheckNullPointerImpl::nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible null pointer dereference: $symbol."); const std::string errmsgdefarg("$symbol:" + varname + "\nPossible null pointer dereference if the default parameter value is used: $symbol"); @@ -465,7 +504,7 @@ void CheckNullPointer::nullPointerError(const Token *tok, const std::string &var } } -void CheckNullPointer::arithmetic() +void CheckNullPointerImpl::arithmetic() { logChecker("CheckNullPointer::arithmetic"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -512,7 +551,7 @@ static std::string arithmeticTypeString(const Token *tok) return "arithmetic"; } -void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) +void CheckNullPointerImpl::pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; @@ -530,7 +569,7 @@ void CheckNullPointer::pointerArithmeticError(const Token* tok, const ValueFlow: inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckNullPointer::redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive) +void CheckNullPointerImpl::redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive) { std::string arithmetic = arithmeticTypeString(tok); std::string errmsg; @@ -571,10 +610,9 @@ namespace { }; } - Check::FileInfo *CheckNullPointer::getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const { - const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, isUnsafeUsage); + const std::list &unsafeUsage = CTU::getUnsafeUsage(tokenizer, settings, ::isUnsafeUsage); if (unsafeUsage.empty()) return nullptr; @@ -601,9 +639,8 @@ bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std:: bool foundErrors = false; (void)settings; // This argument is unused - CheckNullPointer dummy(nullptr, &settings, &errorLogger); - dummy. - logChecker("CheckNullPointer::analyseWholeProgram"); // unusedfunctions + CheckNullPointerImpl dummy(nullptr, &settings, &errorLogger); + //dummy.logChecker("CheckNullPointer::analyseWholeProgram"); // TODO const std::map> callsMap = ctu->getCallsMap(); @@ -642,3 +679,23 @@ bool CheckNullPointer::analyseWholeProgram(const CTU::FileInfo *ctu, const std:: return foundErrors; } + +/** @brief Run checks against the normal token list */ +void CheckNullPointer::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckNullPointerImpl checkNullPointer(&tokenizer, tokenizer.getSettings(), errorLogger); + checkNullPointer.nullPointer(); + checkNullPointer.arithmetic(); + checkNullPointer.nullConstantDereference(); +} + +void CheckNullPointer::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckNullPointerImpl c(nullptr, settings, errorLogger); + c.nullPointerError(nullptr, "pointer", nullptr, false); + c.pointerArithmeticError(nullptr, nullptr, false); + c.redundantConditionWarning(nullptr, nullptr, nullptr, false); +} + +void CheckNullPointer::parseFunctionCall(const Token &tok, std::list &var, const Library *library) +{ + CheckNullPointerImpl::parseFunctionCall(tok, var, library); +} diff --git a/lib/checknullpointer.h b/lib/checknullpointer.h index fec9f5e7bbc..60f3f6c2b06 100644 --- a/lib/checknullpointer.h +++ b/lib/checknullpointer.h @@ -24,25 +24,19 @@ #include "check.h" #include "config.h" -#include "tokenize.h" -#include "vfvalue.h" #include #include class ErrorLogger; -class Library; class Settings; class Token; +class Tokenizer; namespace CTU { class FileInfo; } -namespace tinyxml2 { - class XMLElement; -} - /// @addtogroup Checks /// @{ @@ -54,7 +48,7 @@ class CPPCHECKLIB CheckNullPointer : public Check { public: /** @brief This constructor is used when registering the CheckNullPointer */ - CheckNullPointer() : Check(myName()) {} + CheckNullPointer() : Check("Null pointer") {} /** * Is there a pointer dereference? Everything that should result in @@ -68,42 +62,13 @@ class CPPCHECKLIB CheckNullPointer : public Check { bool isPointerDeRef(const Token *tok, bool &unknown) const; static bool isPointerDeRef(const Token *tok, bool &unknown, const Settings *settings); - -private: - /** - * @brief parse a function call and extract information about variable usage - * @param tok first token - * @param var variables that the function read / write. - * @param library --library files data - */ static void parseFunctionCall(const Token &tok, - std::list &var, - const Library *library); - - /** @brief This constructor is used when running checks. */ - CheckNullPointer(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} + std::list &var, + const Library *library); +private: /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckNullPointer checkNullPointer(&tokenizer, tokenizer.getSettings(), errorLogger); - checkNullPointer.nullPointer(); - checkNullPointer.arithmetic(); - checkNullPointer.nullConstantDereference(); - } - - /** @brief possible null pointer dereference */ - void nullPointer(); - - /** @brief dereferencing null constant (after Tokenizer::simplifyKnownVariables) */ - void nullConstantDereference(); - - void nullPointerError(const Token *tok) { - ValueFlow::Value v(0); - v.setKnown(); - nullPointerError(tok, emptyString, &v, false); - } - void nullPointerError(const Token *tok, const std::string &varname, const ValueFlow::Value* value, bool inconclusive); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const override; @@ -114,17 +79,7 @@ class CPPCHECKLIB CheckNullPointer : public Check { bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; /** Get error messages. Used by --errorlist */ - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckNullPointer c(nullptr, settings, errorLogger); - c.nullPointerError(nullptr, "pointer", nullptr, false); - c.pointerArithmeticError(nullptr, nullptr, false); - c.redundantConditionWarning(nullptr, nullptr, nullptr, false); - } - - /** Name of check */ - static std::string myName() { - return "Null pointer"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; /** class info in WIKI format. Used by --doc */ std::string classInfo() const override { @@ -132,17 +87,6 @@ class CPPCHECKLIB CheckNullPointer : public Check { "- null pointer dereferencing\n" "- undefined null pointer arithmetic\n"; } - - /** - * @brief Does one part of the check for nullPointer(). - * Dereferencing a pointer and then checking if it's NULL.. - */ - void nullPointerByDeRefAndChec(); - - /** undefined null pointer arithmetic */ - void arithmetic(); - void pointerArithmeticError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); - void redundantConditionWarning(const Token* tok, const ValueFlow::Value *value, const Token *condition, bool inconclusive); }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 041b9d022cc..91a695e2c9d 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -21,6 +21,7 @@ #include "checkother.h" #include "astutils.h" +#include "checkimpl.h" #include "fwdanalysis.h" #include "library.h" #include "mathlib.h" @@ -70,6 +71,190 @@ static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Imple static const CWE CWE768(768U); // Incorrect Short Circuit Evaluation static const CWE CWE783(783U); // Operator Precedence Logic Error +namespace { + class CheckOtherImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckOtherImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief Clarify calculation for ".. a * b ? .." */ + void clarifyCalculation(); + + /** @brief Suspicious statement like '*A++;' */ + void clarifyStatement(); + + /** @brief Are there C-style pointer casts in a c++ file? */ + void warningOldStylePointerCast(); + + /** @brief Check for pointer casts to a type with an incompatible binary data representation */ + void invalidPointerCast(); + + /** @brief %Check scope of variables */ + void checkVariableScope(); + bool checkInnerScope(const Token *tok, const Variable* var, bool& used) const; + + /** @brief %Check for comma separated statements in return */ + void checkCommaSeparatedReturn(); + + /** @brief %Check for function parameters that should be passed by reference */ + void checkPassByReference(); + + void checkConstVariable(); + void checkConstPointer(); + + /** @brief Using char variable as array index / as operand in bit operation */ + void checkCharVariable(); + + /** @brief Incomplete statement. A statement that only contains a constant or variable */ + void checkIncompleteStatement(); + + /** @brief %Check zero division*/ + void checkZeroDivision(); + + /** @brief Check for NaN (not-a-number) in an arithmetic expression */ + void checkNanInArithmeticExpression(); + + /** @brief copying to memory or assigning to a variable twice */ + void checkRedundantAssignment(); + + /** @brief %Check for redundant bitwise operation in switch statement*/ + void redundantBitwiseOperationInSwitchError(); + + /** @brief %Check for code like 'case A||B:'*/ + void checkSuspiciousCaseInSwitch(); + + /** @brief %Check for objects that are destroyed immediately */ + void checkMisusedScopedObject(); + + /** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ + void checkDuplicateBranch(); + + /** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ + void checkDuplicateExpression(); + + /** @brief %Check for code that gets never executed, such as duplicate break statements */ + void checkUnreachableCode(); + + /** @brief %Check for testing sign of unsigned variable */ + void checkSignOfUnsignedVariable(); + + /** @brief %Check for suspicious use of semicolon */ + void checkSuspiciousSemicolon(); + + /** @brief %Check for free() operations on invalid memory locations */ + void checkInvalidFree(); + void invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive); + + /** @brief %Check for code creating redundant copies */ + void checkRedundantCopy(); + + /** @brief %Check for bitwise shift with negative right operand */ + void checkNegativeBitwiseShift(); + + /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ + void checkIncompleteArrayFill(); + + /** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ + void checkVarFuncNullUB(); + + /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ + void checkCastIntToCharAndBack(); + + /** @brief %Check for using of comparison functions evaluating always to true or false. */ + void checkComparisonFunctionIsAlwaysTrueOrFalse(); + + /** @brief %Check for redundant pointer operations */ + void checkRedundantPointerOp(); + + /** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */ + void checkInterlockedDecrement(); + + /** @brief %Check for unused labels */ + void checkUnusedLabel(); + + /** @brief %Check for expression that depends on order of evaluation of side effects */ + void checkEvaluationOrder(); + + /** @brief %Check for access of moved or forwarded variable */ + void checkAccessOfMovedVariable(); + + /** @brief %Check if function declaration and definition argument names different */ + void checkFuncArgNamesDifferent(); + + /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ + void checkShadowVariables(); + + void checkKnownArgument(); + + void checkKnownPointerToBool(); + + void checkComparePointers(); + + void checkModuloOfOne(); + + void checkOverlappingWrite(); + void overlappingWriteUnion(const Token *tok); + void overlappingWriteFunction(const Token *tok); + + // Error messages.. + void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); + void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); + void clarifyCalculationError(const Token *tok, const std::string &op); + void clarifyStatementError(const Token* tok); + void cstyleCastError(const Token *tok); + void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); + void passedByValueError(const Variable* var, bool inconclusive); + void constVariableError(const Variable *var, const Function *function); + void constStatementError(const Token *tok, const std::string &type, bool inconclusive); + void signedCharArrayIndexError(const Token *tok); + void unknownSignCharArrayIndexError(const Token *tok); + void charBitOpError(const Token *tok); + void variableScopeError(const Token *tok, const std::string &varname); + void zerodivError(const Token *tok, const ValueFlow::Value *value); + void nanInArithmeticExpressionError(const Token *tok); + void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); + void redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); + void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); + void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); + void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); + void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); + void selfAssignmentError(const Token *tok, const std::string &varname); + void misusedScopeObjectError(const Token *tok, const std::string &varname, bool isAssignment = false); + void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); + void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); + void oppositeExpressionError(const Token *opTok, ErrorPath errors); + void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr = false); + void duplicateValueTernaryError(const Token *tok); + void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); + void duplicateBreakError(const Token *tok, bool inconclusive); + void unreachableCodeError(const Token* tok, const Token* noreturn, bool inconclusive); + void redundantContinueError(const Token* tok); + void unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); + void pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v); + void unsignedPositiveError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); + void pointerPositiveError(const Token *tok, const ValueFlow::Value *v); + void suspiciousSemicolonError(const Token *tok); + void negativeBitwiseShiftError(const Token *tok, int op); + void redundantCopyError(const Token *tok, const std::string &varname); + void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); + void varFuncNullUBError(const Token *tok); + void commaSeparatedReturnError(const Token *tok); + void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive, bool addressOfDeref); + void raceAfterInterlockedDecrementError(const Token* tok); + void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef); + void unknownEvaluationOrder(const Token* tok); + void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); + void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); + void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); + void shadowError(const Token *var, const Token *shadowed, const std::string& type); + void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden); + void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value); + void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); + void checkModuloOfOneError(const Token *tok); + }; +} + //---------------------------------------------------------------------------------- // The return value of fgetc(), getc(), ungetc(), getchar() etc. is an integer value. // If this return value is stored in a character variable and then compared @@ -82,12 +267,12 @@ static const CWE CWE783(783U); // Operator Precedence Logic Error // - http://www.cplusplus.com/reference/cstdio/getchar/ // - http://www.cplusplus.com/reference/cstdio/ungetc/ ... //---------------------------------------------------------------------------------- -void CheckOther::checkCastIntToCharAndBack() +void CheckOtherImpl::checkCastIntToCharAndBack() { if (!mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkCastIntToCharAndBack"); // warning + logChecker("CheckOtherImpl::checkCastIntToCharAndBack"); // warning const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -132,7 +317,7 @@ void CheckOther::checkCastIntToCharAndBack() } } -void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) +void CheckOtherImpl::checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName) { reportError( tok, @@ -152,12 +337,12 @@ void CheckOther::checkCastIntToCharAndBackError(const Token *tok, const std::str //--------------------------------------------------------------------------- // Clarify calculation precedence for ternary operators. //--------------------------------------------------------------------------- -void CheckOther::clarifyCalculation() +void CheckOtherImpl::clarifyCalculation() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::clarifyCalculation"); // style + logChecker("CheckOtherImpl::clarifyCalculation"); // style const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -201,7 +386,7 @@ void CheckOther::clarifyCalculation() } } -void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op) +void CheckOtherImpl::clarifyCalculationError(const Token *tok, const std::string &op) { // suspicious calculation const std::string calc("'a" + op + "b?c:d'"); @@ -223,12 +408,12 @@ void CheckOther::clarifyCalculationError(const Token *tok, const std::string &op //--------------------------------------------------------------------------- // Clarify (meaningless) statements like *foo++; with parentheses. //--------------------------------------------------------------------------- -void CheckOther::clarifyStatement() +void CheckOtherImpl::clarifyStatement() { if (!mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::clarifyStatement"); // warning + logChecker("CheckOtherImpl::clarifyStatement"); // warning const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -249,7 +434,7 @@ void CheckOther::clarifyStatement() } } -void CheckOther::clarifyStatementError(const Token *tok) +void CheckOtherImpl::clarifyStatementError(const Token *tok) { reportError(tok, Severity::warning, "clarifyStatement", "In expression like '*A++' the result of '*' is unused. Did you intend to write '(*A)++;'?\n" "A statement like '*A++;' might not do what you intended. Postfix 'operator++' is executed before 'operator*'. " @@ -259,14 +444,14 @@ void CheckOther::clarifyStatementError(const Token *tok) //--------------------------------------------------------------------------- // Check for suspicious occurrences of 'if(); {}'. //--------------------------------------------------------------------------- -void CheckOther::checkSuspiciousSemicolon() +void CheckOtherImpl::checkSuspiciousSemicolon() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) return; const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); - logChecker("CheckOther::checkSuspiciousSemicolon"); // warning,inconclusive + logChecker("CheckOtherImpl::checkSuspiciousSemicolon"); // warning,inconclusive // Look for "if(); {}", "for(); {}" or "while(); {}" for (const Scope &scope : symbolDatabase->scopeList) { @@ -283,7 +468,7 @@ void CheckOther::checkSuspiciousSemicolon() } } -void CheckOther::suspiciousSemicolonError(const Token* tok) +void CheckOtherImpl::suspiciousSemicolonError(const Token* tok) { reportError(tok, Severity::warning, "suspiciousSemicolon", "Suspicious use of ; at the end of '" + (tok ? tok->str() : std::string()) + "' statement.", CWE398, Certainty::normal); @@ -293,13 +478,13 @@ void CheckOther::suspiciousSemicolonError(const Token* tok) //--------------------------------------------------------------------------- // For C++ code, warn if C-style casts are used on pointer types //--------------------------------------------------------------------------- -void CheckOther::warningOldStylePointerCast() +void CheckOtherImpl::warningOldStylePointerCast() { // Only valid on C++ code if (!mSettings->severity.isEnabled(Severity::style) || !mTokenizer->isCPP()) return; - logChecker("CheckOther::warningOldStylePointerCast"); // style,c++ + logChecker("CheckOtherImpl::warningOldStylePointerCast"); // style,c++ const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -333,7 +518,7 @@ void CheckOther::warningOldStylePointerCast() } } -void CheckOther::cstyleCastError(const Token *tok) +void CheckOtherImpl::cstyleCastError(const Token *tok) { reportError(tok, Severity::style, "cstyleCast", "C-style pointer casting\n" @@ -347,12 +532,12 @@ void CheckOther::cstyleCastError(const Token *tok) // float* f; double* d = (double*)f; <-- Pointer cast to a type with an incompatible binary data representation //--------------------------------------------------------------------------- -void CheckOther::invalidPointerCast() +void CheckOtherImpl::invalidPointerCast() { if (!mSettings->severity.isEnabled(Severity::portability)) return; - logChecker("CheckOther::invalidPointerCast"); // portability + logChecker("CheckOtherImpl::invalidPointerCast"); // portability const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -387,7 +572,7 @@ void CheckOther::invalidPointerCast() } -void CheckOther::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) +void CheckOtherImpl::invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt) { if (toIsInt) { // If we cast something to int*, this can be useful to play with its binary data representation reportError(tok, Severity::portability, "invalidPointerCast", "Casting from " + from + " to " + to + " is not portable due to different binary data representations on different platforms.", CWE704, inconclusive ? Certainty::inconclusive : Certainty::normal); @@ -400,12 +585,12 @@ void CheckOther::invalidPointerCastError(const Token* tok, const std::string& fr // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- -void CheckOther::checkRedundantAssignment() +void CheckOtherImpl::checkRedundantAssignment() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkRedundantAssignment"); // style + logChecker("CheckOtherImpl::checkRedundantAssignment"); // style const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *scope : symbolDatabase->functionScopes) { @@ -515,7 +700,7 @@ void CheckOther::checkRedundantAssignment() } } -void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) +void CheckOtherImpl::redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var) { const std::list callstack = { tok1, tok2 }; reportError(callstack, Severity::performance, "redundantCopy", @@ -523,7 +708,7 @@ void CheckOther::redundantCopyError(const Token *tok1, const Token* tok2, const "Buffer '$symbol' is being written before its old content has been used.", CWE563, Certainty::normal); } -void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) +void CheckOtherImpl::redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is assigned"), ErrorPathItem(tok2, var + " is overwritten") }; if (inconclusive) @@ -537,7 +722,7 @@ void CheckOther::redundantAssignmentError(const Token *tok1, const Token* tok2, "Variable '$symbol' is reassigned a value before the old one has been used.", CWE563, Certainty::normal); } -void CheckOther::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) +void CheckOtherImpl::redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive) { const ErrorPath errorPath = { ErrorPathItem(tok1, var + " is initialized"), ErrorPathItem(tok2, var + " is overwritten") }; reportError(errorPath, Severity::style, "redundantInitialization", @@ -546,7 +731,7 @@ void CheckOther::redundantInitializationError(const Token *tok1, const Token* to inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckOther::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) +void CheckOtherImpl::redundantAssignmentInSwitchError(const Token *tok1, const Token* tok2, const std::string &var) { const ErrorPath errorPath = { ErrorPathItem(tok1, "$symbol is assigned"), ErrorPathItem(tok2, "$symbol is overwritten") }; reportError(errorPath, Severity::style, "redundantAssignInSwitch", @@ -569,12 +754,12 @@ static inline bool isFunctionOrBreakPattern(const Token *tok) return Token::Match(tok, "%name% (") || Token::Match(tok, "break|continue|return|exit|goto|throw"); } -void CheckOther::redundantBitwiseOperationInSwitchError() +void CheckOtherImpl::redundantBitwiseOperationInSwitchError() { if (!mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::redundantBitwiseOperationInSwitch"); // warning + logChecker("CheckOtherImpl::redundantBitwiseOperationInSwitch"); // warning const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -686,7 +871,7 @@ void CheckOther::redundantBitwiseOperationInSwitchError() } } -void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) +void CheckOtherImpl::redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, "redundantBitwiseOperationInSwitch", @@ -698,12 +883,12 @@ void CheckOther::redundantBitwiseOperationInSwitchError(const Token *tok, const //--------------------------------------------------------------------------- // Check for statements like case A||B: in switch() //--------------------------------------------------------------------------- -void CheckOther::checkSuspiciousCaseInSwitch() +void CheckOtherImpl::checkSuspiciousCaseInSwitch() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkSuspiciousCaseInSwitch"); // warning,inconclusive + logChecker("CheckOtherImpl::checkSuspiciousCaseInSwitch"); // warning,inconclusive const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -732,7 +917,7 @@ void CheckOther::checkSuspiciousCaseInSwitch() } } -void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) +void CheckOtherImpl::suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString) { reportError(tok, Severity::warning, "suspiciousCase", "Found suspicious case label in switch(). Operator '" + operatorString + "' probably doesn't work as intended.\n" @@ -745,12 +930,12 @@ void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string // Detect dead code, that follows such a statement. e.g.: // return(0); foo(); //--------------------------------------------------------------------------- -void CheckOther::checkUnreachableCode() +void CheckOtherImpl::checkUnreachableCode() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkUnreachableCode"); // style + logChecker("CheckOtherImpl::checkUnreachableCode"); // style const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -850,7 +1035,7 @@ void CheckOther::checkUnreachableCode() } } -void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) +void CheckOtherImpl::duplicateBreakError(const Token *tok, bool inconclusive) { reportError(tok, Severity::style, "duplicateBreak", "Consecutive return, break, continue, goto or throw statements are unnecessary.\n" @@ -858,7 +1043,7 @@ void CheckOther::duplicateBreakError(const Token *tok, bool inconclusive) "The second statement can never be executed, and so should be removed.", CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckOther::unreachableCodeError(const Token *tok, const Token* noreturn, bool inconclusive) +void CheckOtherImpl::unreachableCodeError(const Token *tok, const Token* noreturn, bool inconclusive) { std::string msg = "Statements following "; if (noreturn && (noreturn->function() || mSettings->library.isnoreturn(noreturn))) @@ -872,7 +1057,7 @@ void CheckOther::unreachableCodeError(const Token *tok, const Token* noreturn, b msg, CWE561, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckOther::redundantContinueError(const Token *tok) +void CheckOtherImpl::redundantContinueError(const Token *tok) { reportError(tok, Severity::style, "redundantContinue", "'continue' is redundant since it is the last statement in a loop.", CWE561, Certainty::normal); @@ -896,7 +1081,7 @@ static bool isSimpleExpr(const Token* tok, const Variable* var, const Settings* //--------------------------------------------------------------------------- // Check scope of variables.. //--------------------------------------------------------------------------- -void CheckOther::checkVariableScope() +void CheckOtherImpl::checkVariableScope() { if (mSettings->clang) return; @@ -911,7 +1096,7 @@ void CheckOther::checkVariableScope() if (mSettings->daca && mTokenizer->isC()) return; - logChecker("CheckOther::checkVariableScope"); // style,notclang + logChecker("CheckOtherImpl::checkVariableScope"); // style,notclang for (const Variable* var : symbolDatabase->variableList()) { if (!var || !var->isLocal() || var->isConst()) @@ -1016,7 +1201,7 @@ void CheckOther::checkVariableScope() } } -bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& used) const +bool CheckOtherImpl::checkInnerScope(const Token *tok, const Variable* var, bool& used) const { const Scope* scope = tok->next()->scope(); bool loopVariable = scope->isLoopScope(); @@ -1114,7 +1299,7 @@ bool CheckOther::checkInnerScope(const Token *tok, const Variable* var, bool& us return true; } -void CheckOther::variableScopeError(const Token *tok, const std::string &varname) +void CheckOtherImpl::variableScopeError(const Token *tok, const std::string &varname) { reportError(tok, Severity::style, @@ -1141,7 +1326,7 @@ void CheckOther::variableScopeError(const Token *tok, const std::string &varname //--------------------------------------------------------------------------- // Comma in return statement: return a+1, b++;. (experimental) //--------------------------------------------------------------------------- -void CheckOther::checkCommaSeparatedReturn() +void CheckOtherImpl::checkCommaSeparatedReturn() { // This is experimental for now. See #5076 if ((true) || !mSettings->severity.isEnabled(Severity::style)) // NOLINT(readability-simplify-boolean-expr) @@ -1168,7 +1353,7 @@ void CheckOther::checkCommaSeparatedReturn() } } -void CheckOther::commaSeparatedReturnError(const Token *tok) +void CheckOtherImpl::commaSeparatedReturnError(const Token *tok) { reportError(tok, Severity::style, @@ -1234,12 +1419,12 @@ static int estimateSize(const Type* type, const Settings* settings, const Symbol }); } -void CheckOther::checkPassByReference() +void CheckOtherImpl::checkPassByReference() { if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC()) return; - logChecker("CheckOther::checkPassByReference"); // performance,c++ + logChecker("CheckOtherImpl::checkPassByReference"); // performance,c++ const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -1289,7 +1474,7 @@ void CheckOther::checkPassByReference() } } -void CheckOther::passedByValueError(const Variable* var, bool inconclusive) +void CheckOtherImpl::passedByValueError(const Variable* var, bool inconclusive) { std::string id = "passedByValue"; std::string msg = "$symbol:" + (var ? var->name() : "") + "\n" @@ -1348,7 +1533,7 @@ static bool isVariableMutableInInitializer(const Token* start, const Token * end return false; } -void CheckOther::checkConstVariable() +void CheckOtherImpl::checkConstVariable() { if (!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) return; @@ -1478,12 +1663,12 @@ static const Token* getVariableChangedStart(const Variable* p) return start; } -void CheckOther::checkConstPointer() +void CheckOtherImpl::checkConstPointer() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkConstPointer"); // style + logChecker("CheckOtherImpl::checkConstPointer"); // style std::vector pointers, nonConstPointers; for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { @@ -1610,7 +1795,7 @@ void CheckOther::checkConstPointer() } } -void CheckOther::constVariableError(const Variable *var, const Function *function) +void CheckOtherImpl::constVariableError(const Variable *var, const Function *function) { if (!var) { reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const"); @@ -1648,14 +1833,14 @@ void CheckOther::constVariableError(const Variable *var, const Function *functio // Check usage of char variables.. //--------------------------------------------------------------------------- -void CheckOther::checkCharVariable() +void CheckOtherImpl::checkCharVariable() { const bool warning = mSettings->severity.isEnabled(Severity::warning); const bool portability = mSettings->severity.isEnabled(Severity::portability); if (!warning && !portability) return; - logChecker("CheckOther::checkCharVariable"); // warning,portability + logChecker("CheckOtherImpl::checkCharVariable"); // warning,portability const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -1699,7 +1884,7 @@ void CheckOther::checkCharVariable() } } -void CheckOther::signedCharArrayIndexError(const Token *tok) +void CheckOtherImpl::signedCharArrayIndexError(const Token *tok) { reportError(tok, Severity::warning, @@ -1710,7 +1895,7 @@ void CheckOther::signedCharArrayIndexError(const Token *tok) "because of sign extension.", CWE128, Certainty::normal); } -void CheckOther::unknownSignCharArrayIndexError(const Token *tok) +void CheckOtherImpl::unknownSignCharArrayIndexError(const Token *tok) { reportError(tok, Severity::portability, @@ -1720,7 +1905,7 @@ void CheckOther::unknownSignCharArrayIndexError(const Token *tok) "treated depending on whether 'char' is signed or unsigned on target platform.", CWE758, Certainty::normal); } -void CheckOther::charBitOpError(const Token *tok) +void CheckOtherImpl::charBitOpError(const Token *tok) { reportError(tok, Severity::warning, @@ -1878,12 +2063,12 @@ static bool isConstTop(const Token *tok) return false; } -void CheckOther::checkIncompleteStatement() +void CheckOtherImpl::checkIncompleteStatement() { if (!mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkIncompleteStatement"); // warning + logChecker("CheckOtherImpl::checkIncompleteStatement"); // warning for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { const Scope *scope = tok->scope(); @@ -1938,7 +2123,7 @@ void CheckOther::checkIncompleteStatement() } } -void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive) +void CheckOtherImpl::constStatementError(const Token *tok, const std::string &type, bool inconclusive) { const Token *valueTok = tok; while (valueTok && valueTok->isCast()) @@ -1987,9 +2172,9 @@ void CheckOther::constStatementError(const Token *tok, const std::string &type, //--------------------------------------------------------------------------- // Detect division by zero. //--------------------------------------------------------------------------- -void CheckOther::checkZeroDivision() +void CheckOtherImpl::checkZeroDivision() { - logChecker("CheckOther::checkZeroDivision"); + logChecker("CheckOtherImpl::checkZeroDivision"); for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand2() || !tok->astOperand1()) @@ -2008,7 +2193,7 @@ void CheckOther::checkZeroDivision() } } -void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) +void CheckOtherImpl::zerodivError(const Token *tok, const ValueFlow::Value *value) { if (!tok && !value) { reportError(tok, Severity::error, "zerodiv", "Division by zero.", CWE369, Certainty::normal); @@ -2037,11 +2222,11 @@ void CheckOther::zerodivError(const Token *tok, const ValueFlow::Value *value) // double d = 1.0 / 0.0 + 100.0; //--------------------------------------------------------------------------- -void CheckOther::checkNanInArithmeticExpression() +void CheckOtherImpl::checkNanInArithmeticExpression() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkNanInArithmeticExpression"); // style + logChecker("CheckOtherImpl::checkNanInArithmeticExpression"); // style for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->str() != "/") continue; @@ -2052,7 +2237,7 @@ void CheckOther::checkNanInArithmeticExpression() } } -void CheckOther::nanInArithmeticExpressionError(const Token *tok) +void CheckOtherImpl::nanInArithmeticExpressionError(const Token *tok) { reportError(tok, Severity::style, "nanInArithmeticExpression", "Using NaN/Inf in a computation.\n" @@ -2063,7 +2248,7 @@ void CheckOther::nanInArithmeticExpressionError(const Token *tok) //--------------------------------------------------------------------------- // Creating instance of classes which are destroyed immediately //--------------------------------------------------------------------------- -void CheckOther::checkMisusedScopedObject() +void CheckOtherImpl::checkMisusedScopedObject() { // Skip this check for .c files if (mTokenizer->isC()) @@ -2072,7 +2257,7 @@ void CheckOther::checkMisusedScopedObject() if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkMisusedScopedObject"); // style,c++ + logChecker("CheckOtherImpl::checkMisusedScopedObject"); // style,c++ auto getConstructorTok = [](const Token* tok, std::string& typeStr) -> const Token* { if (!Token::Match(tok, "[;{}] %name%") || tok->next()->isKeyword()) @@ -2138,7 +2323,7 @@ void CheckOther::checkMisusedScopedObject() } } -void CheckOther::misusedScopeObjectError(const Token *tok, const std::string& varname, bool isAssignment) +void CheckOtherImpl::misusedScopeObjectError(const Token *tok, const std::string& varname, bool isAssignment) { std::string msg = "Instance of '$symbol' object is destroyed immediately"; msg += isAssignment ? ", assignment has no effect." : "."; @@ -2165,7 +2350,7 @@ static const Token * getSingleExpressionInBlock(const Token * tok) // check for duplicate code in if and else branches // if (a) { b = true; } else { b = true; } //----------------------------------------------------------------------------- -void CheckOther::checkDuplicateBranch() +void CheckOtherImpl::checkDuplicateBranch() { // This is inconclusive since in practice most warnings are noise: // * There can be unfixed low-priority todos. The code is fine as it @@ -2178,7 +2363,7 @@ void CheckOther::checkDuplicateBranch() if (!mSettings->severity.isEnabled(Severity::style) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; - logChecker("CheckOther::checkDuplicateBranch"); // style,inconclusive + logChecker("CheckOtherImpl::checkDuplicateBranch"); // style,inconclusive const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2244,7 +2429,7 @@ void CheckOther::checkDuplicateBranch() } } -void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) +void CheckOtherImpl::duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors) { errors.emplace_back(tok2, ""); errors.emplace_back(tok1, ""); @@ -2261,12 +2446,12 @@ void CheckOther::duplicateBranchError(const Token *tok1, const Token *tok2, Erro // char* p = malloc(100); // free(p + 10); //----------------------------------------------------------------------------- -void CheckOther::checkInvalidFree() +void CheckOtherImpl::checkInvalidFree() { std::map inconclusive; std::map allocation; - logChecker("CheckOther::checkInvalidFree"); + logChecker("CheckOtherImpl::checkInvalidFree"); const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2335,7 +2520,7 @@ void CheckOther::checkInvalidFree() } } -void CheckOther::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) +void CheckOtherImpl::invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive) { std::string alloc = allocation; if (alloc != "new") @@ -2378,14 +2563,14 @@ namespace { } } -void CheckOther::checkDuplicateExpression() +void CheckOtherImpl::checkDuplicateExpression() { const bool styleEnabled = mSettings->severity.isEnabled(Severity::style); const bool warningEnabled = mSettings->severity.isEnabled(Severity::warning); if (!styleEnabled && !warningEnabled) return; - logChecker("CheckOther::checkDuplicateExpression"); // style,warning + logChecker("CheckOtherImpl::checkDuplicateExpression"); // style,warning // Parse all executing scopes.. const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2567,7 +2752,7 @@ void CheckOther::checkDuplicateExpression() } } -void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) +void CheckOtherImpl::oppositeExpressionError(const Token *opTok, ErrorPath errors) { errors.emplace_back(opTok, ""); @@ -2579,7 +2764,7 @@ void CheckOther::oppositeExpressionError(const Token *opTok, ErrorPath errors) "determine if it is correct.", CWE398, Certainty::normal); } -void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr) +void CheckOtherImpl::duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr) { errors.emplace_back(opTok, ""); @@ -2606,7 +2791,7 @@ void CheckOther::duplicateExpressionError(const Token *tok1, const Token *tok2, "determine if it is correct.", CWE398, Certainty::normal); } -void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) +void CheckOtherImpl::duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive) { const std::list toks = { tok2, tok1 }; @@ -2620,7 +2805,7 @@ void CheckOther::duplicateAssignExpressionError(const Token *tok1, const Token * "determine if it is correct.", CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) +void CheckOtherImpl::duplicateExpressionTernaryError(const Token *tok, ErrorPath errors) { errors.emplace_back(tok, ""); reportError(errors, Severity::style, "duplicateExpressionTernary", "Same expression in both branches of ternary operator.\n" @@ -2628,14 +2813,14 @@ void CheckOther::duplicateExpressionTernaryError(const Token *tok, ErrorPath err "the same code is executed regardless of the condition.", CWE398, Certainty::normal); } -void CheckOther::duplicateValueTernaryError(const Token *tok) +void CheckOtherImpl::duplicateValueTernaryError(const Token *tok) { reportError(tok, Severity::style, "duplicateValueTernary", "Same value in both branches of ternary operator.\n" "Finding the same value in both branches of ternary operator is suspicious as " "the same code is executed regardless of the condition.", CWE398, Certainty::normal); } -void CheckOther::selfAssignmentError(const Token *tok, const std::string &varname) +void CheckOtherImpl::selfAssignmentError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "selfAssignment", @@ -2652,12 +2837,12 @@ void CheckOther::selfAssignmentError(const Token *tok, const std::string &varnam // Reference: // - http://www.cplusplus.com/reference/cmath/ //----------------------------------------------------------------------------- -void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() +void CheckOtherImpl::checkComparisonFunctionIsAlwaysTrueOrFalse() { if (!mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse"); // warning + logChecker("CheckOtherImpl::checkComparisonFunctionIsAlwaysTrueOrFalse"); // warning const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -2682,7 +2867,7 @@ void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalse() } } } -void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) +void CheckOtherImpl::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result) { const std::string strResult = bool_to_string(result); const CWE cweResult = result ? CWE571 : CWE570; @@ -2697,12 +2882,12 @@ void CheckOther::checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* to //--------------------------------------------------------------------------- // Check testing sign of unsigned variables and pointers. //--------------------------------------------------------------------------- -void CheckOther::checkSignOfUnsignedVariable() +void CheckOtherImpl::checkSignOfUnsignedVariable() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkSignOfUnsignedVariable"); // style + logChecker("CheckOtherImpl::checkSignOfUnsignedVariable"); // style const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2711,13 +2896,13 @@ void CheckOther::checkSignOfUnsignedVariable() for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { const ValueFlow::Value *zeroValue = nullptr; const Token *nonZeroExpr = nullptr; - if (comparisonNonZeroExpressionLessThanZero(tok, &zeroValue, &nonZeroExpr)) { + if (CheckOther::comparisonNonZeroExpressionLessThanZero(tok, &zeroValue, &nonZeroExpr)) { const ValueType* vt = nonZeroExpr->valueType(); if (vt->pointer) pointerLessThanZeroError(tok, zeroValue); else unsignedLessThanZeroError(tok, zeroValue, nonZeroExpr->expressionString()); - } else if (testIfNonZeroExpressionIsPositive(tok, &zeroValue, &nonZeroExpr)) { + } else if (CheckOther::testIfNonZeroExpressionIsPositive(tok, &zeroValue, &nonZeroExpr)) { const ValueType* vt = nonZeroExpr->valueType(); if (vt->pointer) pointerPositiveError(tok, zeroValue); @@ -2772,7 +2957,7 @@ bool CheckOther::testIfNonZeroExpressionIsPositive(const Token *tok, const Value return vt && (vt->pointer || vt->sign == ValueType::UNSIGNED); } -void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) +void CheckOtherImpl::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned less than zero"), Severity::style, "unsignedLessThanZero", "$symbol:" + varname + "\n" @@ -2781,20 +2966,20 @@ void CheckOther::unsignedLessThanZeroError(const Token *tok, const ValueFlow::Va "is either pointless or an error to check if it is.", CWE570, Certainty::normal); } -void CheckOther::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) +void CheckOtherImpl::pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v) { reportError(getErrorPath(tok, v, "Pointer less than zero"), Severity::style, "pointerLessThanZero", "A pointer can not be negative so it is either pointless or an error to check if it is.", CWE570, Certainty::normal); } -void CheckOther::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) +void CheckOtherImpl::unsignedPositiveError(const Token *tok, const ValueFlow::Value * v, const std::string &varname) { reportError(getErrorPath(tok, v, "Unsigned positive"), Severity::style, "unsignedPositive", "$symbol:" + varname + "\n" "Unsigned expression '$symbol' can't be negative so it is unnecessary to test it.", CWE570, Certainty::normal); } -void CheckOther::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) +void CheckOtherImpl::pointerPositiveError(const Token *tok, const ValueFlow::Value * v) { reportError(getErrorPath(tok, v, "Pointer positive"), Severity::style, "pointerPositive", "A pointer can not be negative so it is either pointless or an error to check if it is not.", CWE570, Certainty::normal); @@ -2820,12 +3005,12 @@ static bool constructorTakesReference(const Scope * const classScope) // This check rule works for checking the "const A a = getA()" usage when getA() returns "const A &" or "A &". // In most scenarios, "const A & a = getA()" will be more efficient. //--------------------------------------------------------------------------- -void CheckOther::checkRedundantCopy() +void CheckOtherImpl::checkRedundantCopy() { if (!mSettings->severity.isEnabled(Severity::performance) || mTokenizer->isC() || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; - logChecker("CheckOther::checkRedundantCopy"); // c++,performance,inconclusive + logChecker("CheckOtherImpl::checkRedundantCopy"); // c++,performance,inconclusive const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2879,7 +3064,7 @@ void CheckOther::checkRedundantCopy() } } -void CheckOther::redundantCopyError(const Token *tok,const std::string& varname) +void CheckOtherImpl::redundantCopyError(const Token *tok,const std::string& varname) { reportError(tok, Severity::performance, "redundantCopyLocalConst", "$symbol:" + varname + "\n" @@ -2899,11 +3084,11 @@ static bool isNegative(const Token *tok, const Settings *settings) return tok->valueType() && tok->valueType()->sign == ValueType::SIGNED && tok->getValueLE(-1LL, settings); } -void CheckOther::checkNegativeBitwiseShift() +void CheckOtherImpl::checkNegativeBitwiseShift() { const bool portability = mSettings->severity.isEnabled(Severity::portability); - logChecker("CheckOther::checkNegativeBitwiseShift"); + logChecker("CheckOtherImpl::checkNegativeBitwiseShift"); for (const Token* tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand1() || !tok->astOperand2()) @@ -2939,7 +3124,7 @@ void CheckOther::checkNegativeBitwiseShift() } -void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) +void CheckOtherImpl::negativeBitwiseShiftError(const Token *tok, int op) { if (op == 1) // LHS - this is used by intention in various software, if it @@ -2953,7 +3138,7 @@ void CheckOther::negativeBitwiseShiftError(const Token *tok, int op) //--------------------------------------------------------------------------- // Check for incompletely filled buffers. //--------------------------------------------------------------------------- -void CheckOther::checkIncompleteArrayFill() +void CheckOtherImpl::checkIncompleteArrayFill() { if (!mSettings->certainty.isEnabled(Certainty::inconclusive)) return; @@ -2962,7 +3147,7 @@ void CheckOther::checkIncompleteArrayFill() if (!printPortability && !printWarning) return; - logChecker("CheckOther::checkIncompleteArrayFill"); // warning,portability,inconclusive + logChecker("CheckOtherImpl::checkIncompleteArrayFill"); // warning,portability,inconclusive const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -2999,7 +3184,7 @@ void CheckOther::checkIncompleteArrayFill() } } -void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) +void CheckOtherImpl::incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean) { if (boolean) reportError(tok, Severity::portability, "incompleteArrayFill", @@ -3019,12 +3204,12 @@ void CheckOther::incompleteArrayFillError(const Token* tok, const std::string& b // Detect NULL being passed to variadic function. //--------------------------------------------------------------------------- -void CheckOther::checkVarFuncNullUB() +void CheckOtherImpl::checkVarFuncNullUB() { if (!mSettings->severity.isEnabled(Severity::portability)) return; - logChecker("CheckOther::checkVarFuncNullUB"); // portability + logChecker("CheckOtherImpl::checkVarFuncNullUB"); // portability const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -3057,7 +3242,7 @@ void CheckOther::checkVarFuncNullUB() } } -void CheckOther::varFuncNullUBError(const Token *tok) +void CheckOtherImpl::varFuncNullUBError(const Token *tok) { reportError(tok, Severity::portability, @@ -3104,12 +3289,12 @@ void CheckOther::varFuncNullUBError(const Token *tok) "}", CWE475, Certainty::normal); } -void CheckOther::checkRedundantPointerOp() +void CheckOtherImpl::checkRedundantPointerOp() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkRedundantPointerOp"); // style + logChecker("CheckOtherImpl::checkRedundantPointerOp"); // style for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isExpandedMacro() && tok->str() == "(") @@ -3143,20 +3328,20 @@ void CheckOther::checkRedundantPointerOp() } } -void CheckOther::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive, bool addressOfDeref) +void CheckOtherImpl::redundantPointerOpError(const Token* tok, const std::string &varname, bool inconclusive, bool addressOfDeref) { std::string msg = "$symbol:" + varname + "\nRedundant pointer operation on '$symbol' - it's already a "; msg += addressOfDeref ? "pointer." : "variable."; reportError(tok, Severity::style, "redundantPointerOp", msg, CWE398, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckOther::checkInterlockedDecrement() +void CheckOtherImpl::checkInterlockedDecrement() { if (!mSettings->platform.isWindows()) { return; } - logChecker("CheckOther::checkInterlockedDecrement"); // windows-platform + logChecker("CheckOtherImpl::checkInterlockedDecrement"); // windows-platform for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (tok->isName() && Token::Match(tok, "InterlockedDecrement ( & %name% ) ; if ( %name%|!|0")) { @@ -3190,18 +3375,18 @@ void CheckOther::checkInterlockedDecrement() } } -void CheckOther::raceAfterInterlockedDecrementError(const Token* tok) +void CheckOtherImpl::raceAfterInterlockedDecrementError(const Token* tok) { reportError(tok, Severity::error, "raceAfterInterlockedDecrement", "Race condition: non-interlocked access after InterlockedDecrement(). Use InterlockedDecrement() return value instead.", CWE362, Certainty::normal); } -void CheckOther::checkUnusedLabel() +void CheckOtherImpl::checkUnusedLabel() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkUnusedLabel"); // style,warning + logChecker("CheckOtherImpl::checkUnusedLabel"); // style,warning const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -3219,7 +3404,7 @@ void CheckOther::checkUnusedLabel() } } -void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef) +void CheckOtherImpl::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef) { if (tok && !mSettings->severity.isEnabled(inSwitch ? Severity::warning : Severity::style)) return; @@ -3245,13 +3430,13 @@ void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef } -void CheckOther::checkEvaluationOrder() +void CheckOtherImpl::checkEvaluationOrder() { // This checker is not written according to C++11 sequencing rules if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) return; - logChecker("CheckOther::checkEvaluationOrder"); // C/C++03 + logChecker("CheckOtherImpl::checkEvaluationOrder"); // C/C++03 const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * functionScope : symbolDatabase->functionScopes) { @@ -3320,17 +3505,17 @@ void CheckOther::checkEvaluationOrder() } } -void CheckOther::unknownEvaluationOrder(const Token* tok) +void CheckOtherImpl::unknownEvaluationOrder(const Token* tok) { reportError(tok, Severity::error, "unknownEvaluationOrder", "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); } -void CheckOther::checkAccessOfMovedVariable() +void CheckOtherImpl::checkAccessOfMovedVariable() { if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->severity.isEnabled(Severity::warning)) return; - logChecker("CheckOther::checkAccessOfMovedVariable"); // c++11,warning + logChecker("CheckOtherImpl::checkAccessOfMovedVariable"); // c++11,warning const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { @@ -3371,7 +3556,7 @@ void CheckOther::checkAccessOfMovedVariable() } } -void CheckOther::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) +void CheckOtherImpl::accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive) { if (!tok) { reportError(tok, Severity::warning, "accessMoved", "Access of moved variable 'v'.", CWE672, Certainty::normal); @@ -3400,7 +3585,7 @@ void CheckOther::accessMovedError(const Token *tok, const std::string &varname, -void CheckOther::checkFuncArgNamesDifferent() +void CheckOtherImpl::checkFuncArgNamesDifferent() { const bool style = mSettings->severity.isEnabled(Severity::style); const bool inconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); @@ -3409,7 +3594,7 @@ void CheckOther::checkFuncArgNamesDifferent() if (!(warning || (style && inconclusive))) return; - logChecker("CheckOther::checkFuncArgNamesDifferent"); // style,warning,inconclusive + logChecker("CheckOtherImpl::checkFuncArgNamesDifferent"); // style,warning,inconclusive const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); // check every function @@ -3483,7 +3668,7 @@ void CheckOther::checkFuncArgNamesDifferent() } } -void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg int index, +void CheckOtherImpl::funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition) { std::list tokens = { declaration,definition }; @@ -3494,7 +3679,7 @@ void CheckOther::funcArgNamesDifferent(const std::string & functionName, nonneg (definition ? definition->str() : std::string("B")) + "'.", CWE628, Certainty::inconclusive); } -void CheckOther::funcArgOrderDifferent(const std::string & functionName, +void CheckOtherImpl::funcArgOrderDifferent(const std::string & functionName, const Token* declaration, const Token* definition, const std::vector & declarations, const std::vector & definitions) @@ -3545,11 +3730,11 @@ static const Token *findShadowed(const Scope *scope, const std::string &varname, return shadowed; } -void CheckOther::checkShadowVariables() +void CheckOtherImpl::checkShadowVariables() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkShadowVariables"); // style + logChecker("CheckOtherImpl::checkShadowVariables"); // style const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope & scope : symbolDatabase->scopeList) { if (!scope.isExecutable() || scope.type == Scope::eLambda) @@ -3587,7 +3772,7 @@ void CheckOther::checkShadowVariables() } } -void CheckOther::shadowError(const Token *var, const Token *shadowed, const std::string& type) +void CheckOtherImpl::shadowError(const Token *var, const Token *shadowed, const std::string& type) { ErrorPath errorPath; errorPath.emplace_back(shadowed, "Shadowed declaration"); @@ -3626,11 +3811,11 @@ static bool isVariableExprHidden(const Token* tok) return false; } -void CheckOther::checkKnownArgument() +void CheckOtherImpl::checkKnownArgument() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkKnownArgument"); // style + logChecker("CheckOtherImpl::checkKnownArgument"); // style const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { @@ -3695,7 +3880,7 @@ void CheckOther::checkKnownArgument() } } -void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden) +void CheckOtherImpl::knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden) { if (!tok) { reportError(tok, Severity::style, "knownArgument", "Argument 'x-x' to function 'func' is always 0. It does not matter what value 'x' has."); @@ -3727,11 +3912,11 @@ void CheckOther::knownArgumentError(const Token *tok, const Token *ftok, const V reportError(errorPath, Severity::style, id, errmsg, CWE570, Certainty::normal); } -void CheckOther::checkKnownPointerToBool() +void CheckOtherImpl::checkKnownPointerToBool() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkKnownPointerToBool"); // style + logChecker("CheckOtherImpl::checkKnownPointerToBool"); // style const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope* functionScope : symbolDatabase->functionScopes) { for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { @@ -3757,7 +3942,7 @@ void CheckOther::checkKnownPointerToBool() } } -void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value) +void CheckOtherImpl::knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value) { if (!tok) { reportError(tok, Severity::style, "knownPointerToBool", "Pointer expression 'p' converted to bool is always true."); @@ -3770,9 +3955,9 @@ void CheckOther::knownPointerToBoolError(const Token* tok, const ValueFlow::Valu reportError(errorPath, Severity::style, "knownPointerToBool", errmsg, CWE570, Certainty::normal); } -void CheckOther::checkComparePointers() +void CheckOtherImpl::checkComparePointers() { - logChecker("CheckOther::checkComparePointers"); + logChecker("CheckOtherImpl::checkComparePointers"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { @@ -3807,7 +3992,7 @@ void CheckOther::checkComparePointers() } } -void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) +void CheckOtherImpl::comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2) { ErrorPath errorPath; std::string verb = "Comparing"; @@ -3826,12 +4011,12 @@ void CheckOther::comparePointersError(const Token *tok, const ValueFlow::Value * errorPath, Severity::error, "comparePointers", verb + " pointers that point to different objects", CWE570, Certainty::normal); } -void CheckOther::checkModuloOfOne() +void CheckOtherImpl::checkModuloOfOne() { if (!mSettings->severity.isEnabled(Severity::style)) return; - logChecker("CheckOther::checkModuloOfOne"); // style + logChecker("CheckOtherImpl::checkModuloOfOne"); // style for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { if (!tok->astOperand2() || !tok->astOperand1()) @@ -3848,7 +4033,7 @@ void CheckOther::checkModuloOfOne() } } -void CheckOther::checkModuloOfOneError(const Token *tok) +void CheckOtherImpl::checkModuloOfOneError(const Token *tok) { reportError(tok, Severity::style, "moduloofone", "Modulo of one is always equal to zero"); } @@ -3892,9 +4077,9 @@ static bool getBufAndOffset(const Token *expr, const Token **buf, MathLib::bigin return true; } -void CheckOther::checkOverlappingWrite() +void CheckOtherImpl::checkOverlappingWrite() { - logChecker("CheckOther::checkOverlappingWrite"); + logChecker("CheckOtherImpl::checkOverlappingWrite"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope *functionScope : symbolDatabase->functionScopes) { for (const Token *tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { @@ -3988,13 +4173,160 @@ void CheckOther::checkOverlappingWrite() } } -void CheckOther::overlappingWriteUnion(const Token *tok) +void CheckOtherImpl::overlappingWriteUnion(const Token *tok) { reportError(tok, Severity::error, "overlappingWriteUnion", "Overlapping read/write of union is undefined behavior"); } -void CheckOther::overlappingWriteFunction(const Token *tok) +void CheckOtherImpl::overlappingWriteFunction(const Token *tok) { const std::string &funcname = tok ? tok->str() : emptyString; reportError(tok, Severity::error, "overlappingWriteFunction", "Overlapping read/write in " + funcname + "() is undefined behavior"); } + +void CheckOther::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckOtherImpl checkOther(&tokenizer, tokenizer.getSettings(), errorLogger); + + // Checks + checkOther.warningOldStylePointerCast(); + checkOther.invalidPointerCast(); + checkOther.checkCharVariable(); + checkOther.checkRedundantAssignment(); + checkOther.redundantBitwiseOperationInSwitchError(); + checkOther.checkSuspiciousCaseInSwitch(); + checkOther.checkDuplicateBranch(); + checkOther.checkDuplicateExpression(); + checkOther.checkUnreachableCode(); + checkOther.checkSuspiciousSemicolon(); + checkOther.checkVariableScope(); + checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) + checkOther.checkIncompleteArrayFill(); + checkOther.checkVarFuncNullUB(); + checkOther.checkNanInArithmeticExpression(); + checkOther.checkCommaSeparatedReturn(); + checkOther.checkRedundantPointerOp(); + checkOther.checkZeroDivision(); + checkOther.checkNegativeBitwiseShift(); + checkOther.checkInterlockedDecrement(); + checkOther.checkUnusedLabel(); + checkOther.checkEvaluationOrder(); + checkOther.checkFuncArgNamesDifferent(); + checkOther.checkShadowVariables(); + checkOther.checkKnownArgument(); + checkOther.checkKnownPointerToBool(); + checkOther.checkComparePointers(); + checkOther.checkIncompleteStatement(); + checkOther.checkRedundantCopy(); + checkOther.clarifyCalculation(); + checkOther.checkPassByReference(); + checkOther.checkConstVariable(); + checkOther.checkConstPointer(); + checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); + checkOther.checkInvalidFree(); + checkOther.clarifyStatement(); + checkOther.checkCastIntToCharAndBack(); + checkOther.checkMisusedScopedObject(); + checkOther.checkAccessOfMovedVariable(); + checkOther.checkModuloOfOne(); + checkOther.checkOverlappingWrite(); +} + +void CheckOther::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckOtherImpl c(nullptr, settings, errorLogger); + + ErrorPath errorPath; + + // error + c.zerodivError(nullptr, nullptr); + c.misusedScopeObjectError(nullptr, "varname"); + c.invalidPointerCastError(nullptr, "float *", "double *", false, false); + c.negativeBitwiseShiftError(nullptr, 1); + c.negativeBitwiseShiftError(nullptr, 2); + c.raceAfterInterlockedDecrementError(nullptr); + c.invalidFreeError(nullptr, "malloc", false); + c.overlappingWriteUnion(nullptr); + c.overlappingWriteFunction(nullptr); + + //performance + c.redundantCopyError(nullptr, "varname"); + c.redundantCopyError(nullptr, nullptr, "var"); + + // style/warning + c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); + c.checkCastIntToCharAndBackError(nullptr, "func_name"); + c.cstyleCastError(nullptr); + c.passedByValueError(nullptr, false); + c.constVariableError(nullptr, nullptr); + c.constStatementError(nullptr, "type", false); + c.signedCharArrayIndexError(nullptr); + c.unknownSignCharArrayIndexError(nullptr); + c.charBitOpError(nullptr); + c.variableScopeError(nullptr, "varname"); + c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); + c.suspiciousCaseInSwitchError(nullptr, "||"); + c.selfAssignmentError(nullptr, "varname"); + c.clarifyCalculationError(nullptr, "+"); + c.clarifyStatementError(nullptr); + c.duplicateBranchError(nullptr, nullptr, errorPath); + c.duplicateAssignExpressionError(nullptr, nullptr, true); + c.oppositeExpressionError(nullptr, errorPath); + c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath); + c.duplicateValueTernaryError(nullptr); + c.duplicateExpressionTernaryError(nullptr, errorPath); + c.duplicateBreakError(nullptr, false); + c.unreachableCodeError(nullptr, nullptr, false); + c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); + c.unsignedPositiveError(nullptr, nullptr, "varname"); + c.pointerLessThanZeroError(nullptr, nullptr); + c.pointerPositiveError(nullptr, nullptr); + c.suspiciousSemicolonError(nullptr); + c.incompleteArrayFillError(nullptr, "buffer", "memset", false); + c.varFuncNullUBError(nullptr); + c.nanInArithmeticExpressionError(nullptr); + c.commaSeparatedReturnError(nullptr); + c.redundantPointerOpError(nullptr, "varname", false, /*addressOfDeref*/ true); + 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); + c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); + c.shadowError(nullptr, nullptr, "variable"); + c.shadowError(nullptr, nullptr, "function"); + c.shadowError(nullptr, nullptr, "argument"); + c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); + c.knownPointerToBoolError(nullptr, nullptr); + c.comparePointersError(nullptr, nullptr, nullptr); + c.redundantAssignmentError(nullptr, nullptr, "var", false); + c.redundantInitializationError(nullptr, nullptr, "var", false); + + const std::vector nullvec; + c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); + c.checkModuloOfOneError(nullptr); +} + +void CheckOther::checkCharVariable(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckOtherImpl c(tokenizer, settings, errorLogger); + c.checkCharVariable(); +} + +void CheckOther::warningOldStylePointerCast(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckOtherImpl c(tokenizer, settings, errorLogger); + c.warningOldStylePointerCast(); +} + +void CheckOther::invalidPointerCast(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckOtherImpl c(tokenizer, settings, errorLogger); + c.invalidPointerCast(); +} + +void CheckOther::checkIncompleteStatement(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger) +{ + CheckOtherImpl c(tokenizer, settings, errorLogger); + c.checkIncompleteStatement(); +} diff --git a/lib/checkother.h b/lib/checkother.h index 2499ef1b581..eb6a3caf74b 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -28,7 +28,6 @@ #include "tokenize.h" #include -#include namespace ValueFlow { class Value; @@ -53,7 +52,7 @@ class CPPCHECKLIB CheckOther : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckOther() : Check(myName()) {} + CheckOther() : Check("Other") {} /** Is expression a comparison that checks if a nonzero (unsigned/pointer) expression is less than zero? */ static bool comparisonNonZeroExpressionLessThanZero(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr); @@ -62,313 +61,10 @@ class CPPCHECKLIB CheckOther : public Check { static bool testIfNonZeroExpressionIsPositive(const Token *tok, const ValueFlow::Value **zeroValue, const Token **nonZeroExpr); private: - /** @brief This constructor is used when running checks. */ - CheckOther(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckOther checkOther(&tokenizer, tokenizer.getSettings(), errorLogger); - - // Checks - checkOther.warningOldStylePointerCast(); - checkOther.invalidPointerCast(); - checkOther.checkCharVariable(); - checkOther.checkRedundantAssignment(); - checkOther.redundantBitwiseOperationInSwitchError(); - checkOther.checkSuspiciousCaseInSwitch(); - checkOther.checkDuplicateBranch(); - checkOther.checkDuplicateExpression(); - checkOther.checkUnreachableCode(); - checkOther.checkSuspiciousSemicolon(); - checkOther.checkVariableScope(); - checkOther.checkSignOfUnsignedVariable(); // don't ignore casts (#3574) - checkOther.checkIncompleteArrayFill(); - checkOther.checkVarFuncNullUB(); - checkOther.checkNanInArithmeticExpression(); - checkOther.checkCommaSeparatedReturn(); - checkOther.checkRedundantPointerOp(); - checkOther.checkZeroDivision(); - checkOther.checkNegativeBitwiseShift(); - checkOther.checkInterlockedDecrement(); - checkOther.checkUnusedLabel(); - checkOther.checkEvaluationOrder(); - checkOther.checkFuncArgNamesDifferent(); - checkOther.checkShadowVariables(); - checkOther.checkKnownArgument(); - checkOther.checkKnownPointerToBool(); - checkOther.checkComparePointers(); - checkOther.checkIncompleteStatement(); - checkOther.checkRedundantCopy(); - checkOther.clarifyCalculation(); - checkOther.checkPassByReference(); - checkOther.checkConstVariable(); - checkOther.checkConstPointer(); - checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse(); - checkOther.checkInvalidFree(); - checkOther.clarifyStatement(); - checkOther.checkCastIntToCharAndBack(); - checkOther.checkMisusedScopedObject(); - checkOther.checkAccessOfMovedVariable(); - checkOther.checkModuloOfOne(); - checkOther.checkOverlappingWrite(); - } - - /** @brief Clarify calculation for ".. a * b ? .." */ - void clarifyCalculation(); - - /** @brief Suspicious statement like '*A++;' */ - void clarifyStatement(); - - /** @brief Are there C-style pointer casts in a c++ file? */ - void warningOldStylePointerCast(); - - /** @brief Check for pointer casts to a type with an incompatible binary data representation */ - void invalidPointerCast(); - - /** @brief %Check scope of variables */ - void checkVariableScope(); - bool checkInnerScope(const Token *tok, const Variable* var, bool& used) const; - - /** @brief %Check for comma separated statements in return */ - void checkCommaSeparatedReturn(); - - /** @brief %Check for function parameters that should be passed by reference */ - void checkPassByReference(); - - void checkConstVariable(); - void checkConstPointer(); - - /** @brief Using char variable as array index / as operand in bit operation */ - void checkCharVariable(); - - /** @brief Incomplete statement. A statement that only contains a constant or variable */ - void checkIncompleteStatement(); - - /** @brief %Check zero division*/ - void checkZeroDivision(); - - /** @brief Check for NaN (not-a-number) in an arithmetic expression */ - void checkNanInArithmeticExpression(); - - /** @brief copying to memory or assigning to a variable twice */ - void checkRedundantAssignment(); - - /** @brief %Check for redundant bitwise operation in switch statement*/ - void redundantBitwiseOperationInSwitchError(); - - /** @brief %Check for code like 'case A||B:'*/ - void checkSuspiciousCaseInSwitch(); - - /** @brief %Check for objects that are destroyed immediately */ - void checkMisusedScopedObject(); - - /** @brief %Check for suspicious code where if and else branch are the same (e.g "if (a) b = true; else b = true;") */ - void checkDuplicateBranch(); - - /** @brief %Check for suspicious code with the same expression on both sides of operator (e.g "if (a && a)") */ - void checkDuplicateExpression(); - - /** @brief %Check for code that gets never executed, such as duplicate break statements */ - void checkUnreachableCode(); - - /** @brief %Check for testing sign of unsigned variable */ - void checkSignOfUnsignedVariable(); - - /** @brief %Check for suspicious use of semicolon */ - void checkSuspiciousSemicolon(); - - /** @brief %Check for free() operations on invalid memory locations */ - void checkInvalidFree(); - void invalidFreeError(const Token *tok, const std::string &allocation, bool inconclusive); - - /** @brief %Check for code creating redundant copies */ - void checkRedundantCopy(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check for bitwise shift with negative right operand */ - void checkNegativeBitwiseShift(); - - /** @brief %Check for buffers that are filled incompletely with memset and similar functions */ - void checkIncompleteArrayFill(); - - /** @brief %Check that variadic function calls don't use NULL. If NULL is \#defined as 0 and the function expects a pointer, the behaviour is undefined. */ - void checkVarFuncNullUB(); - - /** @brief %Check to avoid casting a return value to unsigned char and then back to integer type. */ - void checkCastIntToCharAndBack(); - - /** @brief %Check for using of comparison functions evaluating always to true or false. */ - void checkComparisonFunctionIsAlwaysTrueOrFalse(); - - /** @brief %Check for redundant pointer operations */ - void checkRedundantPointerOp(); - - /** @brief %Check for race condition with non-interlocked access after InterlockedDecrement() */ - void checkInterlockedDecrement(); - - /** @brief %Check for unused labels */ - void checkUnusedLabel(); - - /** @brief %Check for expression that depends on order of evaluation of side effects */ - void checkEvaluationOrder(); - - /** @brief %Check for access of moved or forwarded variable */ - void checkAccessOfMovedVariable(); - - /** @brief %Check if function declaration and definition argument names different */ - void checkFuncArgNamesDifferent(); - - /** @brief %Check for shadow variables. Less noisy than gcc/clang -Wshadow. */ - void checkShadowVariables(); - - void checkKnownArgument(); - - void checkKnownPointerToBool(); - - void checkComparePointers(); - - void checkModuloOfOne(); - - void checkOverlappingWrite(); - void overlappingWriteUnion(const Token *tok); - void overlappingWriteFunction(const Token *tok); - - // Error messages.. - void checkComparisonFunctionIsAlwaysTrueOrFalseError(const Token* tok, const std::string &functionName, const std::string &varName, const bool result); - void checkCastIntToCharAndBackError(const Token *tok, const std::string &strFunctionName); - void clarifyCalculationError(const Token *tok, const std::string &op); - void clarifyStatementError(const Token* tok); - void cstyleCastError(const Token *tok); - void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); - void passedByValueError(const Variable* var, bool inconclusive); - void constVariableError(const Variable *var, const Function *function); - void constStatementError(const Token *tok, const std::string &type, bool inconclusive); - void signedCharArrayIndexError(const Token *tok); - void unknownSignCharArrayIndexError(const Token *tok); - void charBitOpError(const Token *tok); - void variableScopeError(const Token *tok, const std::string &varname); - void zerodivError(const Token *tok, const ValueFlow::Value *value); - void nanInArithmeticExpressionError(const Token *tok); - void redundantAssignmentError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); - void redundantInitializationError(const Token *tok1, const Token* tok2, const std::string& var, bool inconclusive); - void redundantAssignmentInSwitchError(const Token *tok1, const Token *tok2, const std::string &var); - void redundantCopyError(const Token *tok1, const Token* tok2, const std::string& var); - void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname); - void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString); - void selfAssignmentError(const Token *tok, const std::string &varname); - void misusedScopeObjectError(const Token *tok, const std::string &varname, bool isAssignment = false); - void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors); - void duplicateAssignExpressionError(const Token *tok1, const Token *tok2, bool inconclusive); - void oppositeExpressionError(const Token *opTok, ErrorPath errors); - void duplicateExpressionError(const Token *tok1, const Token *tok2, const Token *opTok, ErrorPath errors, bool hasMultipleExpr = false); - void duplicateValueTernaryError(const Token *tok); - void duplicateExpressionTernaryError(const Token *tok, ErrorPath errors); - void duplicateBreakError(const Token *tok, bool inconclusive); - void unreachableCodeError(const Token* tok, const Token* noreturn, bool inconclusive); - void redundantContinueError(const Token* tok); - void unsignedLessThanZeroError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); - void pointerLessThanZeroError(const Token *tok, const ValueFlow::Value *v); - void unsignedPositiveError(const Token *tok, const ValueFlow::Value *v, const std::string &varname); - void pointerPositiveError(const Token *tok, const ValueFlow::Value *v); - void suspiciousSemicolonError(const Token *tok); - void negativeBitwiseShiftError(const Token *tok, int op); - void redundantCopyError(const Token *tok, const std::string &varname); - void incompleteArrayFillError(const Token* tok, const std::string& buffer, const std::string& function, bool boolean); - void varFuncNullUBError(const Token *tok); - void commaSeparatedReturnError(const Token *tok); - void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive, bool addressOfDeref); - void raceAfterInterlockedDecrementError(const Token* tok); - void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef); - void unknownEvaluationOrder(const Token* tok); - void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); - void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); - void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); - void shadowError(const Token *var, const Token *shadowed, const std::string& type); - void knownArgumentError(const Token *tok, const Token *ftok, const ValueFlow::Value *value, const std::string &varexpr, bool isVariableExpressionHidden); - void knownPointerToBoolError(const Token* tok, const ValueFlow::Value* value); - void comparePointersError(const Token *tok, const ValueFlow::Value *v1, const ValueFlow::Value *v2); - void checkModuloOfOneError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckOther c(nullptr, settings, errorLogger); - - ErrorPath errorPath; - - // error - c.zerodivError(nullptr, nullptr); - c.misusedScopeObjectError(nullptr, "varname"); - c.invalidPointerCastError(nullptr, "float *", "double *", false, false); - c.negativeBitwiseShiftError(nullptr, 1); - c.negativeBitwiseShiftError(nullptr, 2); - c.raceAfterInterlockedDecrementError(nullptr); - c.invalidFreeError(nullptr, "malloc", false); - c.overlappingWriteUnion(nullptr); - c.overlappingWriteFunction(nullptr); - - //performance - c.redundantCopyError(nullptr, "varname"); - c.redundantCopyError(nullptr, nullptr, "var"); - - // style/warning - c.checkComparisonFunctionIsAlwaysTrueOrFalseError(nullptr, "isless","varName",false); - c.checkCastIntToCharAndBackError(nullptr, "func_name"); - c.cstyleCastError(nullptr); - c.passedByValueError(nullptr, false); - c.constVariableError(nullptr, nullptr); - c.constStatementError(nullptr, "type", false); - c.signedCharArrayIndexError(nullptr); - c.unknownSignCharArrayIndexError(nullptr); - c.charBitOpError(nullptr); - c.variableScopeError(nullptr, "varname"); - c.redundantAssignmentInSwitchError(nullptr, nullptr, "var"); - c.suspiciousCaseInSwitchError(nullptr, "||"); - c.selfAssignmentError(nullptr, "varname"); - c.clarifyCalculationError(nullptr, "+"); - c.clarifyStatementError(nullptr); - c.duplicateBranchError(nullptr, nullptr, errorPath); - c.duplicateAssignExpressionError(nullptr, nullptr, true); - c.oppositeExpressionError(nullptr, errorPath); - c.duplicateExpressionError(nullptr, nullptr, nullptr, errorPath); - c.duplicateValueTernaryError(nullptr); - c.duplicateExpressionTernaryError(nullptr, errorPath); - c.duplicateBreakError(nullptr, false); - c.unreachableCodeError(nullptr, nullptr, false); - c.unsignedLessThanZeroError(nullptr, nullptr, "varname"); - c.unsignedPositiveError(nullptr, nullptr, "varname"); - c.pointerLessThanZeroError(nullptr, nullptr); - c.pointerPositiveError(nullptr, nullptr); - c.suspiciousSemicolonError(nullptr); - c.incompleteArrayFillError(nullptr, "buffer", "memset", false); - c.varFuncNullUBError(nullptr); - c.nanInArithmeticExpressionError(nullptr); - c.commaSeparatedReturnError(nullptr); - c.redundantPointerOpError(nullptr, "varname", false, /*addressOfDeref*/ true); - 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); - c.redundantBitwiseOperationInSwitchError(nullptr, "varname"); - c.shadowError(nullptr, nullptr, "variable"); - c.shadowError(nullptr, nullptr, "function"); - c.shadowError(nullptr, nullptr, "argument"); - c.knownArgumentError(nullptr, nullptr, nullptr, "x", false); - c.knownPointerToBoolError(nullptr, nullptr); - c.comparePointersError(nullptr, nullptr, nullptr); - c.redundantAssignmentError(nullptr, nullptr, "var", false); - c.redundantInitializationError(nullptr, nullptr, "var", false); - - const std::vector nullvec; - c.funcArgOrderDifferent("function", nullptr, nullptr, nullvec, nullvec); - c.checkModuloOfOneError(nullptr); - } - - static std::string myName() { - return "Other"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Other checks\n" @@ -428,6 +124,12 @@ class CPPCHECKLIB CheckOther : public Check { "- calculating modulo of one.\n" "- known function argument, suspicious calculation.\n"; } + + // for testing + static void checkCharVariable(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void warningOldStylePointerCast(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void invalidPointerCast(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); + static void checkIncompleteStatement(Tokenizer* tokenizer, const Settings *settings, ErrorLogger *errorLogger); }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkpostfixoperator.cpp b/lib/checkpostfixoperator.cpp index eb8d57a39e2..be4f081c491 100644 --- a/lib/checkpostfixoperator.cpp +++ b/lib/checkpostfixoperator.cpp @@ -23,10 +23,12 @@ #include "checkpostfixoperator.h" +#include "checkimpl.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" #include "token.h" +#include "tokenize.h" #include @@ -38,12 +40,25 @@ namespace { CheckPostfixOperator instance; } - // CWE ids used static const CWE CWE398(398U); // Indicator of Poor Code Quality +namespace { + class CheckPostfixOperatorImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckPostfixOperatorImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** Check postfix operators */ + void postfixOperator(); + + /** Report Error */ + void postfixOperatorError(const Token *tok); + }; +} -void CheckPostfixOperator::postfixOperator() +void CheckPostfixOperatorImpl::postfixOperator() { if (!mSettings->severity.isEnabled(Severity::performance)) return; @@ -77,7 +92,7 @@ void CheckPostfixOperator::postfixOperator() //--------------------------------------------------------------------------- -void CheckPostfixOperator::postfixOperatorError(const Token *tok) +void CheckPostfixOperatorImpl::postfixOperatorError(const Token *tok) { reportError(tok, Severity::performance, "postfixOperator", "Prefer prefix ++/-- operators for non-primitive types.\n" @@ -87,3 +102,17 @@ void CheckPostfixOperator::postfixOperatorError(const Token *tok) "involves keeping a copy of the previous value around and " "adds a little extra code.", CWE398, Certainty::normal); } + + +void CheckPostfixOperator::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + if (tokenizer.isC()) + return; + + CheckPostfixOperatorImpl checkPostfixOperator(&tokenizer, tokenizer.getSettings(), errorLogger); + checkPostfixOperator.postfixOperator(); +} + +void CheckPostfixOperator::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckPostfixOperatorImpl c(nullptr, settings, errorLogger); + c.postfixOperatorError(nullptr); +} diff --git a/lib/checkpostfixoperator.h b/lib/checkpostfixoperator.h index 2c163ecfc01..617998859e0 100644 --- a/lib/checkpostfixoperator.h +++ b/lib/checkpostfixoperator.h @@ -24,13 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -44,36 +43,12 @@ class CPPCHECKLIB CheckPostfixOperator : public Check { public: /** This constructor is used when registering the CheckPostfixOperator */ - CheckPostfixOperator() : Check(myName()) {} + CheckPostfixOperator() : Check("Using postfix operators") {} private: - /** This constructor is used when running checks. */ - CheckPostfixOperator(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (tokenizer.isC()) - return; - - CheckPostfixOperator checkPostfixOperator(&tokenizer, tokenizer.getSettings(), errorLogger); - checkPostfixOperator.postfixOperator(); - } - - /** Check postfix operators */ - void postfixOperator(); - - /** Report Error */ - void postfixOperatorError(const Token *tok); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckPostfixOperator c(nullptr, settings, errorLogger); - c.postfixOperatorError(nullptr); - } - - static std::string myName() { - return "Using postfix operators"; - } + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Warn if using postfix operators ++ or -- rather than prefix operator\n"; } diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index 848b8946cbe..e4fb38090cb 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -20,6 +20,7 @@ //--------------------------------------------------------------------------- #include "checksizeof.h" +#include "checkimpl.h" #include "errortypes.h" #include "library.h" #include "settings.h" @@ -43,8 +44,56 @@ namespace { static const CWE CWE467(467U); // Use of sizeof() on a Pointer Type static const CWE CWE682(682U); // Incorrect Calculation //--------------------------------------------------------------------------- + +namespace { + class CheckSizeofImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckSizeofImpl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for 'sizeof sizeof ..' */ + void sizeofsizeof(); + + /** @brief %Check for calculations inside sizeof */ + void sizeofCalculation(); + + /** @brief %Check for function call inside sizeof */ + void sizeofFunction(); + + /** @brief %Check for suspicious calculations with sizeof results */ + void suspiciousSizeofCalculation(); + + /** @brief %Check for using sizeof with array given as function argument */ + void checkSizeofForArrayParameter(); + + /** @brief %Check for using sizeof of a variable when allocating it */ + void checkSizeofForPointerSize(); + + /** @brief %Check for using sizeof with numeric given as function argument */ + void checkSizeofForNumericParameter(); + + /** @brief %Check for using sizeof(void) */ + void sizeofVoid(); + + // Error messages.. + void sizeofsizeofError(const Token* tok); + void sizeofCalculationError(const Token* tok, bool inconclusive); + void sizeofFunctionError(const Token* tok); + void multiplySizeofError(const Token* tok); + void divideSizeofError(const Token* tok); + void sizeofForArrayParameterError(const Token* tok); + void sizeofForPointerError(const Token* tok, const std::string &varname); + void divideBySizeofError(const Token* tok, const std::string &memfunc); + void sizeofForNumericParameterError(const Token* tok); + void sizeofVoidError(const Token *tok); + void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); + void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype); + }; +} + //--------------------------------------------------------------------------- -void CheckSizeof::checkSizeofForNumericParameter() +void CheckSizeofImpl::checkSizeofForNumericParameter() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -62,7 +111,7 @@ void CheckSizeof::checkSizeofForNumericParameter() } } -void CheckSizeof::sizeofForNumericParameterError(const Token *tok) +void CheckSizeofImpl::sizeofForNumericParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithnumericparameter", "Suspicious usage of 'sizeof' with a numeric constant as parameter.\n" @@ -74,7 +123,7 @@ void CheckSizeof::sizeofForNumericParameterError(const Token *tok) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- -void CheckSizeof::checkSizeofForArrayParameter() +void CheckSizeofImpl::checkSizeofForArrayParameter() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -99,7 +148,7 @@ void CheckSizeof::checkSizeofForArrayParameter() } } -void CheckSizeof::sizeofForArrayParameterError(const Token *tok) +void CheckSizeofImpl::sizeofForArrayParameterError(const Token *tok) { reportError(tok, Severity::warning, "sizeofwithsilentarraypointer", "Using 'sizeof' on array given as function argument " @@ -115,7 +164,7 @@ void CheckSizeof::sizeofForArrayParameterError(const Token *tok) ); } -void CheckSizeof::checkSizeofForPointerSize() +void CheckSizeofImpl::checkSizeofForPointerSize() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -266,7 +315,7 @@ void CheckSizeof::checkSizeofForPointerSize() } } -void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &varname) +void CheckSizeofImpl::sizeofForPointerError(const Token *tok, const std::string &varname) { reportError(tok, Severity::warning, "pointerSize", "Size of pointer '" + varname + "' used instead of size of its data.\n" @@ -275,7 +324,7 @@ void CheckSizeof::sizeofForPointerError(const Token *tok, const std::string &var "write 'sizeof(*" + varname + ")'.", CWE467, Certainty::normal); } -void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfunc) +void CheckSizeofImpl::divideBySizeofError(const Token *tok, const std::string &memfunc) { reportError(tok, Severity::warning, "sizeofDivisionMemfunc", "Division by result of sizeof(). " + memfunc + "() expects a size in bytes, did you intend to multiply instead?", CWE682, Certainty::normal); @@ -283,7 +332,7 @@ void CheckSizeof::divideBySizeofError(const Token *tok, const std::string &memfu //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -void CheckSizeof::sizeofsizeof() +void CheckSizeofImpl::sizeofsizeof() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -298,7 +347,7 @@ void CheckSizeof::sizeofsizeof() } } -void CheckSizeof::sizeofsizeofError(const Token *tok) +void CheckSizeofImpl::sizeofsizeofError(const Token *tok) { reportError(tok, Severity::warning, "sizeofsizeof", "Calling 'sizeof' on 'sizeof'.\n" @@ -309,7 +358,7 @@ void CheckSizeof::sizeofsizeofError(const Token *tok) //----------------------------------------------------------------------------- -void CheckSizeof::sizeofCalculation() +void CheckSizeofImpl::sizeofCalculation() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -347,7 +396,7 @@ void CheckSizeof::sizeofCalculation() } } -void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) +void CheckSizeofImpl::sizeofCalculationError(const Token *tok, bool inconclusive) { reportError(tok, Severity::warning, "sizeofCalculation", "Found calculation inside sizeof().", CWE682, inconclusive ? Certainty::inconclusive : Certainty::normal); @@ -355,7 +404,7 @@ void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) //----------------------------------------------------------------------------- -void CheckSizeof::sizeofFunction() +void CheckSizeofImpl::sizeofFunction() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -389,7 +438,7 @@ void CheckSizeof::sizeofFunction() } } -void CheckSizeof::sizeofFunctionError(const Token *tok) +void CheckSizeofImpl::sizeofFunctionError(const Token *tok) { reportError(tok, Severity::warning, "sizeofFunctionCall", "Found function call inside sizeof().", CWE682, Certainty::normal); @@ -398,7 +447,7 @@ void CheckSizeof::sizeofFunctionError(const Token *tok) //----------------------------------------------------------------------------- // Check for code like sizeof()*sizeof() or sizeof(ptr)/value //----------------------------------------------------------------------------- -void CheckSizeof::suspiciousSizeofCalculation() +void CheckSizeofImpl::suspiciousSizeofCalculation() { if (!mSettings->severity.isEnabled(Severity::warning) || !mSettings->certainty.isEnabled(Certainty::inconclusive)) return; @@ -428,13 +477,13 @@ void CheckSizeof::suspiciousSizeofCalculation() } } -void CheckSizeof::multiplySizeofError(const Token *tok) +void CheckSizeofImpl::multiplySizeofError(const Token *tok) { reportError(tok, Severity::warning, "multiplySizeof", "Multiplying sizeof() with sizeof() indicates a logic error.", CWE682, Certainty::inconclusive); } -void CheckSizeof::divideSizeofError(const Token *tok) +void CheckSizeofImpl::divideSizeofError(const Token *tok) { reportError(tok, Severity::warning, "divideSizeof", "Division of result of sizeof() on pointer type.\n" @@ -442,7 +491,7 @@ void CheckSizeof::divideSizeofError(const Token *tok) "not the size of the memory area it points to.", CWE682, Certainty::inconclusive); } -void CheckSizeof::sizeofVoid() +void CheckSizeofImpl::sizeofVoid() { if (!mSettings->severity.isEnabled(Severity::portability)) return; @@ -480,23 +529,53 @@ void CheckSizeof::sizeofVoid() } } -void CheckSizeof::sizeofVoidError(const Token *tok) +void CheckSizeofImpl::sizeofVoidError(const Token *tok) { const std::string message = "Behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofVoid", message + "\n" + verbose, CWE682, Certainty::normal); } -void CheckSizeof::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) +void CheckSizeofImpl::sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname) { const std::string message = "'*" + varname + "' is of type 'void', the behaviour of 'sizeof(void)' is not covered by the ISO C standard."; const std::string verbose = message + " A value for 'sizeof(void)' is defined only as part of a GNU C extension, which defines 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "sizeofDereferencedVoidPointer", message + "\n" + verbose, CWE682, Certainty::normal); } -void CheckSizeof::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) +void CheckSizeofImpl::arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype) { const std::string message = "'$symbol' is of type '" + vartype + "'. When using void pointers in calculations, the behaviour is undefined."; const std::string verbose = message + " Arithmetic operations on 'void *' is a GNU C extension, which defines the 'sizeof(void)' to be 1."; reportError(tok, Severity::portability, "arithOperationsOnVoidPointer", "$symbol:" + varname + '\n' + message + '\n' + verbose, CWE467, Certainty::normal); } + +void CheckSizeof::runChecks(const Tokenizer& tokenizer, ErrorLogger* errorLogger) { + CheckSizeofImpl checkSizeof(&tokenizer, tokenizer.getSettings(), errorLogger); + + // Checks + checkSizeof.sizeofsizeof(); + checkSizeof.sizeofCalculation(); + checkSizeof.sizeofFunction(); + checkSizeof.suspiciousSizeofCalculation(); + checkSizeof.checkSizeofForArrayParameter(); + checkSizeof.checkSizeofForPointerSize(); + checkSizeof.checkSizeofForNumericParameter(); + checkSizeof.sizeofVoid(); +} + +void CheckSizeof::getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const { + CheckSizeofImpl c(nullptr, settings, errorLogger); + c.sizeofForArrayParameterError(nullptr); + c.sizeofForPointerError(nullptr, "varname"); + c.divideBySizeofError(nullptr, "memset"); + c.sizeofForNumericParameterError(nullptr); + c.sizeofsizeofError(nullptr); + c.sizeofCalculationError(nullptr, false); + c.sizeofFunctionError(nullptr); + c.multiplySizeofError(nullptr); + c.divideSizeofError(nullptr); + c.sizeofVoidError(nullptr); + c.sizeofDereferencedVoidPointerError(nullptr, "varname"); + c.arithOperationsOnVoidPointerError(nullptr, "varname", "vartype"); +} diff --git a/lib/checksizeof.h b/lib/checksizeof.h index eaf395c21e5..2d06522358c 100644 --- a/lib/checksizeof.h +++ b/lib/checksizeof.h @@ -24,102 +24,28 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ - /** @brief checks on usage of sizeof() operator */ class CPPCHECKLIB CheckSizeof : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckSizeof() : Check(myName()) {} + CheckSizeof() : Check("Sizeof") {} private: - /** @brief This constructor is used when running checks. */ - CheckSizeof(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer& tokenizer, ErrorLogger* errorLogger) override { - CheckSizeof checkSizeof(&tokenizer, tokenizer.getSettings(), errorLogger); - - // Checks - checkSizeof.sizeofsizeof(); - checkSizeof.sizeofCalculation(); - checkSizeof.sizeofFunction(); - checkSizeof.suspiciousSizeofCalculation(); - checkSizeof.checkSizeofForArrayParameter(); - checkSizeof.checkSizeofForPointerSize(); - checkSizeof.checkSizeofForNumericParameter(); - checkSizeof.sizeofVoid(); - } - - /** @brief %Check for 'sizeof sizeof ..' */ - void sizeofsizeof(); - - /** @brief %Check for calculations inside sizeof */ - void sizeofCalculation(); - - /** @brief %Check for function call inside sizeof */ - void sizeofFunction(); - - /** @brief %Check for suspicious calculations with sizeof results */ - void suspiciousSizeofCalculation(); + void runChecks(const Tokenizer& tokenizer, ErrorLogger* errorLogger) override; - /** @brief %Check for using sizeof with array given as function argument */ - void checkSizeofForArrayParameter(); - - /** @brief %Check for using sizeof of a variable when allocating it */ - void checkSizeofForPointerSize(); - - /** @brief %Check for using sizeof with numeric given as function argument */ - void checkSizeofForNumericParameter(); - - /** @brief %Check for using sizeof(void) */ - void sizeofVoid(); - - // Error messages.. - void sizeofsizeofError(const Token* tok); - void sizeofCalculationError(const Token* tok, bool inconclusive); - void sizeofFunctionError(const Token* tok); - void multiplySizeofError(const Token* tok); - void divideSizeofError(const Token* tok); - void sizeofForArrayParameterError(const Token* tok); - void sizeofForPointerError(const Token* tok, const std::string &varname); - void divideBySizeofError(const Token* tok, const std::string &memfunc); - void sizeofForNumericParameterError(const Token* tok); - void sizeofVoidError(const Token *tok); - void sizeofDereferencedVoidPointerError(const Token *tok, const std::string &varname); - void arithOperationsOnVoidPointerError(const Token* tok, const std::string &varname, const std::string &vartype); - - void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { - CheckSizeof c(nullptr, settings, errorLogger); - c.sizeofForArrayParameterError(nullptr); - c.sizeofForPointerError(nullptr, "varname"); - c.divideBySizeofError(nullptr, "memset"); - c.sizeofForNumericParameterError(nullptr); - c.sizeofsizeofError(nullptr); - c.sizeofCalculationError(nullptr, false); - c.sizeofFunctionError(nullptr); - c.multiplySizeofError(nullptr); - c.divideSizeofError(nullptr); - c.sizeofVoidError(nullptr); - c.sizeofDereferencedVoidPointerError(nullptr, "varname"); - c.arithOperationsOnVoidPointerError(nullptr, "varname", "vartype"); - } - - static std::string myName() { - return "Sizeof"; - } + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override; std::string classInfo() const override { return "sizeof() usage checks\n" diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 7e0b9547d85..2089bc584b8 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -19,6 +19,7 @@ #include "checkstl.h" #include "astutils.h" +#include "checkimpl.h" #include "errortypes.h" #include "library.h" #include "mathlib.h" @@ -64,6 +65,166 @@ static const CWE CWE825(825U); // Expired Pointer Dereference static const CWE CWE833(833U); // Deadlock static const CWE CWE834(834U); // Excessive Iteration +namespace { + class CheckStlImpl : public CheckImpl { + public: + /** This constructor is used when running checks. */ + CheckStlImpl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** Accessing container out of bounds using ValueFlow */ + void outOfBounds(); + + /** Accessing container out of bounds, following index expression */ + void outOfBoundsIndexExpression(); + + /** + * Finds errors like this: + * for (unsigned ii = 0; ii <= foo.size(); ++ii) + */ + void stlOutOfBounds(); + + /** + * negative index for array like containers + */ + void negativeIndex(); + + /** + * Finds errors like this: + * for (it = foo.begin(); it != bar.end(); ++it) + */ + void iterators(); + + void invalidContainer(); + + bool checkIteratorPair(const Token* tok1, const Token* tok2); + + /** + * Mismatching containers: + * std::find(foo.begin(), bar.end(), x) + */ + void mismatchingContainers(); + + void mismatchingContainerIterator(); + + /** + * Dangerous usage of erase. The iterator is invalidated by erase so + * it is bad to dereference it after the erase. + */ + void erase(); + void eraseCheckLoopVar(const Scope& scope, const Variable* var); + + /** + * bad condition.. "it < alist.end()" + */ + void stlBoundaries(); + + /** if (a.find(x)) - possibly incorrect condition */ + void if_find(); + + void checkFindInsert(); + + /** + * Suggest using empty() instead of checking size() against zero for containers. + * Item 4 from Scott Meyers book "Effective STL". + */ + void size(); + + /** + * Check for redundant condition 'if (ints.find(1) != ints.end()) ints.remove(123);' + * */ + void redundantCondition(); + + /** + * @brief Missing inner comparison, when incrementing iterator inside loop + * Dangers: + * - may increment iterator beyond end + * - may unintentionally skip elements in list/set etc + */ + void missingComparison(); + + /** Check for common mistakes when using the function string::c_str() */ + void string_c_str(); + + /** @brief %Check calls that using them is useless */ + void uselessCalls(); + + /** @brief %Check for dereferencing an iterator that is invalid */ + void checkDereferenceInvalidIterator(); + void checkDereferenceInvalidIterator2(); + + /** + * Dereferencing an erased iterator + * @param erased token where the erase occurs + * @param deref token where the dereference occurs + * @param itername iterator name + * @param inconclusive inconclusive flag + */ + void dereferenceErasedError(const Token* erased, const Token* deref, const std::string& itername, bool inconclusive); + + /** @brief Look for loops that can replaced with std algorithms */ + void useStlAlgorithm(); + + void knownEmptyContainer(); + + void checkMutexes(); + + bool isContainerSize(const Token *containerToken, const Token *expr) const; + bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; + + void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); + void string_c_strThrowError(const Token* tok); + void string_c_strError(const Token* tok); + void string_c_strReturn(const Token* tok); + void string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype = "std::string"); + void string_c_strConstructor(const Token* tok, const std::string& argtype = "std::string"); + void string_c_strAssignment(const Token* tok, const std::string& argtype = "std::string"); + void string_c_strConcat(const Token* tok); + void string_c_strStream(const Token* tok); + + void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue); + void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); + void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); + void negativeIndexError(const Token* tok, const ValueFlow::Value& index); + void invalidIteratorError(const Token* tok, const std::string& iteratorName); + void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); + void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); + void mismatchingContainerIteratorError(const Token* tok, const Token* iterTok); + void mismatchingContainersError(const Token* tok1, const Token* tok2); + void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); + void sameIteratorExpressionError(const Token *tok); + void stlBoundariesError(const Token* tok); + void if_findError(const Token* tok, bool str); + void checkFindInsertError(const Token *tok); + void sizeError(const Token* tok); + void redundantIfRemoveError(const Token* tok); + void invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath); + void invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath); + void invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath); + + void uselessCallsReturnValueError(const Token* tok, const std::string& varname, const std::string& function); + void uselessCallsSwapError(const Token* tok, const std::string& varname); + enum class SubstrErrorType { EMPTY, COPY, PREFIX, PREFIX_CONCAT }; + void uselessCallsSubstrError(const Token* tok, SubstrErrorType type); + void uselessCallsEmptyError(const Token* tok); + void uselessCallsRemoveError(const Token* tok, const std::string& function); + void uselessCallsConstructorError(const Token* tok); + + void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName); + void dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); + + void useStlAlgorithmError(const Token *tok, const std::string &algoName); + + void knownEmptyContainerError(const Token *tok, const std::string& algo); + + void globalLockGuardError(const Token *tok); + void localMutexError(const Token *tok); + + + }; +} + static bool isElementAccessYield(Library::Container::Yield yield) { return contains({Library::Container::Yield::ITEM, Library::Container::Yield::AT_INDEX}, yield); @@ -130,7 +291,7 @@ static const Token* getContainerFromSize(const Library::Container* container, co return nullptr; } -void CheckStl::outOfBounds() +void CheckStlImpl::outOfBounds() { logChecker("CheckStl::outOfBounds"); @@ -218,7 +379,7 @@ static std::string indexValueString(const ValueFlow::Value& indexValue, const st return indexString; } -void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue) +void CheckStlImpl::outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue) { // Do not warn if both the container size and index value are possible if (containerSize && indexValue && containerSize->isPossible() && indexValue->isPossible()) @@ -277,7 +438,7 @@ void CheckStl::outOfBoundsError(const Token *tok, const std::string &containerNa (containerSize && containerSize->isInconclusive()) || (indexValue && indexValue->isInconclusive()) ? Certainty::inconclusive : Certainty::normal); } -bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) const +bool CheckStlImpl::isContainerSize(const Token *containerToken, const Token *expr) const { if (!Token::simpleMatch(expr, "( )")) return false; @@ -288,7 +449,7 @@ bool CheckStl::isContainerSize(const Token *containerToken, const Token *expr) c return containerToken->valueType()->container->getYield(expr->previous()->str()) == Library::Container::Yield::SIZE; } -bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr) const +bool CheckStlImpl::isContainerSizeGE(const Token * containerToken, const Token *expr) const { if (!expr) return false; @@ -317,7 +478,7 @@ bool CheckStl::isContainerSizeGE(const Token * containerToken, const Token *expr return false; } -void CheckStl::outOfBoundsIndexExpression() +void CheckStlImpl::outOfBoundsIndexExpression() { logChecker("CheckStl::outOfBoundsIndexExpression"); for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { @@ -337,7 +498,7 @@ void CheckStl::outOfBoundsIndexExpression() } } -void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) +void CheckStlImpl::outOfBoundsIndexExpressionError(const Token *tok, const Token *index) { const std::string varname = tok ? tok->str() : std::string("var"); const std::string i = index ? index->expressionString() : std::string(varname + ".size()"); @@ -355,12 +516,12 @@ void CheckStl::outOfBoundsIndexExpressionError(const Token *tok, const Token *in // Error message for bad iterator usage.. -void CheckStl::invalidIteratorError(const Token *tok, const std::string &iteratorName) +void CheckStlImpl::invalidIteratorError(const Token *tok, const std::string &iteratorName) { reportError(tok, Severity::error, "invalidIterator1", "$symbol:"+iteratorName+"\nInvalid iterator: $symbol", CWE664, Certainty::normal); } -void CheckStl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) +void CheckStlImpl::iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2) { reportError(tok, Severity::error, "iterators1", "$symbol:" + containerName1 + "\n" @@ -368,7 +529,7 @@ void CheckStl::iteratorsError(const Token* tok, const std::string& containerName "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); } -void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) +void CheckStlImpl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2) { std::list callstack = { tok, containerTok }; reportError(callstack, Severity::error, "iterators2", @@ -377,7 +538,7 @@ void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const "Same iterator is used with different containers '" + containerName1 + "' and '" + containerName2 + "'.", CWE664, Certainty::normal); } -void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) +void CheckStlImpl::iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName) { std::list callstack = { tok, containerTok }; reportError(callstack, @@ -391,7 +552,7 @@ void CheckStl::iteratorsError(const Token* tok, const Token* containerTok, const } // Error message used when dereferencing an iterator that has been erased.. -void CheckStl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive) +void CheckStlImpl::dereferenceErasedError(const Token *erased, const Token* deref, const std::string &itername, bool inconclusive) { if (erased) { std::list callstack = { deref, erased }; @@ -461,7 +622,7 @@ static bool isVector(const Token* tok) return Token::simpleMatch(decltok, "std :: vector"); } -void CheckStl::iterators() +void CheckStlImpl::iterators() { logChecker("CheckStl::iterators"); @@ -646,7 +807,7 @@ void CheckStl::iterators() } } -void CheckStl::mismatchingContainerIteratorError(const Token* tok, const Token* iterTok) +void CheckStlImpl::mismatchingContainerIteratorError(const Token* tok, const Token* iterTok) { const std::string container(tok ? tok->expressionString() : std::string("v1")); const std::string iter(iterTok ? iterTok->expressionString() : std::string("it")); @@ -659,7 +820,7 @@ void CheckStl::mismatchingContainerIteratorError(const Token* tok, const Token* } // Error message for bad iterator usage.. -void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2) +void CheckStlImpl::mismatchingContainersError(const Token* tok1, const Token* tok2) { const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); @@ -671,7 +832,7 @@ void CheckStl::mismatchingContainersError(const Token* tok1, const Token* tok2) Certainty::normal); } -void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2) +void CheckStlImpl::mismatchingContainerExpressionError(const Token *tok1, const Token *tok2) { const std::string expr1(tok1 ? tok1->expressionString() : std::string("v1")); const std::string expr2(tok2 ? tok2->expressionString() : std::string("v2")); @@ -680,7 +841,7 @@ void CheckStl::mismatchingContainerExpressionError(const Token *tok1, const Toke expr1 + "' and '" + expr2 + "' are used together.", CWE664, Certainty::normal); } -void CheckStl::sameIteratorExpressionError(const Token *tok) +void CheckStlImpl::sameIteratorExpressionError(const Token *tok) { reportError(tok, Severity::style, "sameIteratorExpression", "Same iterators expression are used for algorithm.", CWE664, Certainty::normal); } @@ -719,7 +880,7 @@ static ValueFlow::Value getLifetimeIteratorValue(const Token* tok, MathLib::bigi return ValueFlow::Value{}; } -bool CheckStl::checkIteratorPair(const Token* tok1, const Token* tok2) +bool CheckStlImpl::checkIteratorPair(const Token* tok1, const Token* tok2) { if (!tok1) return false; @@ -767,7 +928,7 @@ namespace { }; } -void CheckStl::mismatchingContainers() +void CheckStlImpl::mismatchingContainers() { logChecker("CheckStl::misMatchingContainers"); @@ -827,7 +988,7 @@ void CheckStl::mismatchingContainers() } } -void CheckStl::mismatchingContainerIterator() +void CheckStlImpl::mismatchingContainerIterator() { logChecker("CheckStl::misMatchingContainerIterator"); @@ -1072,7 +1233,7 @@ static const Token* endOfExpression(const Token* tok) return endToken; } -void CheckStl::invalidContainer() +void CheckStlImpl::invalidContainer() { logChecker("CheckStl::invalidContainer"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -1182,7 +1343,7 @@ void CheckStl::invalidContainer() } } -void CheckStl::invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath) +void CheckStlImpl::invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath) { const std::string method = tok ? tok->str() : "erase"; errorPath.emplace_back(loopTok, "Iterating container here."); @@ -1197,7 +1358,7 @@ void CheckStl::invalidContainerLoopError(const Token* tok, const Token* loopTok, reportError(errorPath, Severity::error, "invalidContainerLoop", msg, CWE664, Certainty::normal); } -void CheckStl::invalidContainerError(const Token *tok, const Token * /*contTok*/, const ValueFlow::Value *val, ErrorPath errorPath) +void CheckStlImpl::invalidContainerError(const Token *tok, const Token * /*contTok*/, const ValueFlow::Value *val, ErrorPath errorPath) { const bool inconclusive = val ? val->isInconclusive() : false; if (val) @@ -1207,7 +1368,7 @@ void CheckStl::invalidContainerError(const Token *tok, const Token * /*contTok*/ reportError(errorPath, Severity::error, "invalidContainer", msg + " that may be invalid.", CWE664, inconclusive ? Certainty::inconclusive : Certainty::normal); } -void CheckStl::invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath) +void CheckStlImpl::invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath) { std::string name = contTok ? contTok->expressionString() : "x"; std::string msg = "Reference to " + name; @@ -1215,7 +1376,7 @@ void CheckStl::invalidContainerReferenceError(const Token* tok, const Token* con reportError(errorPath, Severity::error, "invalidContainerReference", msg + " that may be invalid.", CWE664, Certainty::normal); } -void CheckStl::stlOutOfBounds() +void CheckStlImpl::stlOutOfBounds() { logChecker("CheckStl::stlOutOfBounds"); @@ -1301,7 +1462,7 @@ void CheckStl::stlOutOfBounds() } } -void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) +void CheckStlImpl::stlOutOfBoundsError(const Token *tok, const std::string &num, const std::string &var, bool at) { if (at) reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol.at(" + num + ") is out of bounds.", CWE788, Certainty::normal); @@ -1309,7 +1470,7 @@ void CheckStl::stlOutOfBoundsError(const Token *tok, const std::string &num, con reportError(tok, Severity::error, "stlOutOfBounds", "$symbol:" + var + "\nWhen " + num + "==$symbol.size(), $symbol[" + num + "] is out of bounds.", CWE788, Certainty::normal); } -void CheckStl::negativeIndex() +void CheckStlImpl::negativeIndex() { logChecker("CheckStl::negativeIndex"); @@ -1333,7 +1494,7 @@ void CheckStl::negativeIndex() } } -void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &index) +void CheckStlImpl::negativeIndexError(const Token *tok, const ValueFlow::Value &index) { const ErrorPath errorPath = getErrorPath(tok, &index, "Negative array index"); std::ostringstream errmsg; @@ -1347,7 +1508,7 @@ void CheckStl::negativeIndexError(const Token *tok, const ValueFlow::Value &inde reportError(errorPath, severity, "negativeContainerIndex", errmsg.str(), CWE786, certainty); } -void CheckStl::erase() +void CheckStlImpl::erase() { logChecker("CheckStl::erase"); @@ -1368,7 +1529,7 @@ void CheckStl::erase() } } -void CheckStl::eraseCheckLoopVar(const Scope &scope, const Variable *var) +void CheckStlImpl::eraseCheckLoopVar(const Scope &scope, const Variable *var) { bool inconclusiveType=false; if (!isIterator(var, inconclusiveType)) @@ -1412,7 +1573,7 @@ void CheckStl::eraseCheckLoopVar(const Scope &scope, const Variable *var) } } -void CheckStl::stlBoundaries() +void CheckStlImpl::stlBoundaries() { logChecker("CheckStl::stlBoundaries"); @@ -1437,7 +1598,7 @@ void CheckStl::stlBoundaries() } // Error message for bad boundary usage.. -void CheckStl::stlBoundariesError(const Token *tok) +void CheckStlImpl::stlBoundariesError(const Token *tok) { reportError(tok, Severity::error, "stlBoundaries", "Dangerous comparison using operator< on iterator.\n" @@ -1466,7 +1627,7 @@ static bool if_findCompare(const Token * const tokBack, bool stdStringLike) return false; } -void CheckStl::if_find() +void CheckStlImpl::if_find() { const bool printWarning = mSettings->severity.isEnabled(Severity::warning); const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); @@ -1543,7 +1704,7 @@ void CheckStl::if_find() } -void CheckStl::if_findError(const Token *tok, bool str) +void CheckStlImpl::if_findError(const Token *tok, bool str) { if (str && mSettings->standards.cpp >= Standards::CPP20) reportError(tok, Severity::performance, "stlIfStrFind", @@ -1648,7 +1809,7 @@ static const Token *findInsertValue(const Token *tok, const Token *containerTok, return nullptr; } -void CheckStl::checkFindInsert() +void CheckStlImpl::checkFindInsert() { if (!mSettings->severity.isEnabled(Severity::performance)) return; @@ -1694,7 +1855,7 @@ void CheckStl::checkFindInsert() } } -void CheckStl::checkFindInsertError(const Token *tok) +void CheckStlImpl::checkFindInsertError(const Token *tok) { std::string replaceExpr; if (tok && Token::simpleMatch(tok->astParent(), "=") && tok == tok->astParent()->astOperand2() && Token::simpleMatch(tok->astParent()->astOperand1(), "[")) { @@ -1726,7 +1887,7 @@ static bool isCpp03ContainerSizeSlow(const Token *tok) return var && var->isStlType("list"); } -void CheckStl::size() +void CheckStlImpl::size() { if (!mSettings->severity.isEnabled(Severity::performance)) return; @@ -1775,7 +1936,7 @@ void CheckStl::size() } } -void CheckStl::sizeError(const Token *tok) +void CheckStlImpl::sizeError(const Token *tok) { const std::string varname = tok ? tok->str() : std::string("list"); reportError(tok, Severity::performance, "stlSize", @@ -1787,7 +1948,7 @@ void CheckStl::sizeError(const Token *tok) "guaranteed to take constant time.", CWE398, Certainty::normal); } -void CheckStl::redundantCondition() +void CheckStlImpl::redundantCondition() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1820,7 +1981,7 @@ void CheckStl::redundantCondition() } } -void CheckStl::redundantIfRemoveError(const Token *tok) +void CheckStlImpl::redundantIfRemoveError(const Token *tok) { reportError(tok, Severity::style, "redundantIfRemove", "Redundant checking of STL container element existence before removing it.\n" @@ -1828,7 +1989,7 @@ void CheckStl::redundantIfRemoveError(const Token *tok) "It is safe to call the remove method on a non-existing element.", CWE398, Certainty::normal); } -void CheckStl::missingComparison() +void CheckStlImpl::missingComparison() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -1892,7 +2053,7 @@ void CheckStl::missingComparison() } } -void CheckStl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2) +void CheckStlImpl::missingComparisonError(const Token *incrementToken1, const Token *incrementToken2) { std::list callstack = { incrementToken1,incrementToken2 }; @@ -1924,7 +2085,7 @@ namespace { }; } -void CheckStl::string_c_str() +void CheckStlImpl::string_c_str() { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); @@ -2126,25 +2287,25 @@ void CheckStl::string_c_str() } } -void CheckStl::string_c_strThrowError(const Token* tok) +void CheckStlImpl::string_c_strThrowError(const Token* tok) { reportError(tok, Severity::error, "stlcstrthrow", "Dangerous usage of c_str(). The value returned by c_str() is invalid after throwing exception.\n" "Dangerous usage of c_str(). The string is destroyed after the c_str() call so the thrown pointer is invalid."); } -void CheckStl::string_c_strError(const Token* tok) +void CheckStlImpl::string_c_strError(const Token* tok) { reportError(tok, Severity::error, "stlcstr", "Dangerous usage of c_str(). The value returned by c_str() is invalid after this call.\n" "Dangerous usage of c_str(). The c_str() return value is only valid until its string is deleted.", CWE664, Certainty::normal); } -void CheckStl::string_c_strReturn(const Token* tok) +void CheckStlImpl::string_c_strReturn(const Token* tok) { reportError(tok, Severity::performance, "stlcstrReturn", "Returning the result of c_str() in a function that returns std::string is slow and redundant.\n" "The conversion from const char* as returned by c_str() to std::string creates an unnecessary string copy. Solve that by directly returning the string.", CWE704, Certainty::normal); } -void CheckStl::string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype) +void CheckStlImpl::string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype) { std::ostringstream oss; oss << "Passing the result of c_str() to a function that takes " << argtype << " as argument no. " << number << " is slow and redundant.\n" @@ -2152,28 +2313,28 @@ void CheckStl::string_c_strParam(const Token* tok, nonneg int number, const std: reportError(tok, Severity::performance, "stlcstrParam", oss.str(), CWE704, Certainty::normal); } -void CheckStl::string_c_strConstructor(const Token* tok, const std::string& argtype) +void CheckStlImpl::string_c_strConstructor(const Token* tok, const std::string& argtype) { std::string msg = "Constructing a " + argtype + " from the result of c_str() is slow and redundant.\n" "Constructing a " + argtype + " from const char* requires a call to strlen(). Solve that by directly passing the string."; reportError(tok, Severity::performance, "stlcstrConstructor", msg, CWE704, Certainty::normal); } -void CheckStl::string_c_strAssignment(const Token* tok, const std::string& argtype) +void CheckStlImpl::string_c_strAssignment(const Token* tok, const std::string& argtype) { std::string msg = "Assigning the result of c_str() to a " + argtype + " is slow and redundant.\n" "Assigning a const char* to a " + argtype + " requires a call to strlen(). Solve that by directly assigning the string."; reportError(tok, Severity::performance, "stlcstrAssignment", msg, CWE704, Certainty::normal); } -void CheckStl::string_c_strConcat(const Token* tok) +void CheckStlImpl::string_c_strConcat(const Token* tok) { std::string msg = "Concatenating the result of c_str() and a std::string is slow and redundant.\n" "Concatenating a const char* with a std::string requires a call to strlen(). Solve that by directly concatenating the strings."; reportError(tok, Severity::performance, "stlcstrConcat", msg, CWE704, Certainty::normal); } -void CheckStl::string_c_strStream(const Token* tok) +void CheckStlImpl::string_c_strStream(const Token* tok) { std::string msg = "Passing the result of c_str() to a stream is slow and redundant.\n" "Passing a const char* to a stream requires a call to strlen(). Solve that by directly passing the string."; @@ -2194,7 +2355,7 @@ namespace { } -void CheckStl::uselessCalls() +void CheckStlImpl::uselessCalls() { const bool printPerformance = mSettings->severity.isEnabled(Severity::performance); const bool printWarning = mSettings->severity.isEnabled(Severity::warning); @@ -2255,7 +2416,7 @@ void CheckStl::uselessCalls() } -void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function) +void CheckStlImpl::uselessCallsReturnValueError(const Token *tok, const std::string &varname, const std::string &function) { std::ostringstream errmsg; errmsg << "$symbol:" << varname << '\n'; @@ -2268,7 +2429,7 @@ void CheckStl::uselessCallsReturnValueError(const Token *tok, const std::string reportError(tok, Severity::warning, "uselessCallsCompare", errmsg.str(), CWE628, Certainty::normal); } -void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varname) +void CheckStlImpl::uselessCallsSwapError(const Token *tok, const std::string &varname) { reportError(tok, Severity::performance, "uselessCallsSwap", "$symbol:" + varname + "\n" @@ -2278,7 +2439,7 @@ void CheckStl::uselessCallsSwapError(const Token *tok, const std::string &varnam "code is inefficient. Is the object or the parameter wrong here?", CWE628, Certainty::normal); } -void CheckStl::uselessCallsSubstrError(const Token *tok, SubstrErrorType type) +void CheckStlImpl::uselessCallsSubstrError(const Token *tok, SubstrErrorType type) { std::string msg = "Ineffective call of function 'substr' because "; switch (type) { @@ -2298,18 +2459,18 @@ void CheckStl::uselessCallsSubstrError(const Token *tok, SubstrErrorType type) reportError(tok, Severity::performance, "uselessCallsSubstr", msg, CWE398, Certainty::normal); } -void CheckStl::uselessCallsConstructorError(const Token *tok) +void CheckStlImpl::uselessCallsConstructorError(const Token *tok) { const std::string msg = "Inefficient constructor call: container '" + tok->str() + "' is assigned a partial copy of itself. Use erase() or resize() instead."; reportError(tok, Severity::performance, "uselessCallsConstructor", msg, CWE398, Certainty::normal); } -void CheckStl::uselessCallsEmptyError(const Token *tok) +void CheckStlImpl::uselessCallsEmptyError(const Token *tok) { reportError(tok, Severity::warning, "uselessCallsEmpty", "Ineffective call of function 'empty()'. Did you intend to call 'clear()' instead?", CWE398, Certainty::normal); } -void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& function) +void CheckStlImpl::uselessCallsRemoveError(const Token *tok, const std::string& function) { reportError(tok, Severity::warning, "uselessCallsRemove", "$symbol:" + function + "\n" @@ -2320,7 +2481,7 @@ void CheckStl::uselessCallsRemoveError(const Token *tok, const std::string& func // Check for iterators being dereferenced before being checked for validity. // E.g. if (*i && i != str.end()) { } -void CheckStl::checkDereferenceInvalidIterator() +void CheckStlImpl::checkDereferenceInvalidIterator() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -2384,7 +2545,7 @@ void CheckStl::checkDereferenceInvalidIterator() } -void CheckStl::checkDereferenceInvalidIterator2() +void CheckStlImpl::checkDereferenceInvalidIterator2() { const bool printInconclusive = (mSettings->certainty.isEnabled(Certainty::inconclusive)); @@ -2481,7 +2642,7 @@ void CheckStl::checkDereferenceInvalidIterator2() } } -void CheckStl::dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) +void CheckStlImpl::dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive) { const std::string& varname = tok ? tok->expressionString() : "var"; const std::string errmsgcond("$symbol:" + varname + '\n' + ValueFlow::eitherTheConditionIsRedundant(value ? value->condition : nullptr) + " or there is possible dereference of an invalid iterator: $symbol."); @@ -2510,7 +2671,7 @@ void CheckStl::dereferenceInvalidIteratorError(const Token* tok, const ValueFlow } } -void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName) +void CheckStlImpl::dereferenceInvalidIteratorError(const Token* deref, const std::string &iterName) { reportError(deref, Severity::warning, "derefInvalidIterator", @@ -2519,7 +2680,7 @@ void CheckStl::dereferenceInvalidIteratorError(const Token* deref, const std::st "Possible dereference of an invalid iterator: $symbol. Make sure to check that the iterator is valid before dereferencing it - not after.", CWE825, Certainty::normal); } -void CheckStl::useStlAlgorithmError(const Token *tok, const std::string &algoName) +void CheckStlImpl::useStlAlgorithmError(const Token *tok, const std::string &algoName) { reportError(tok, Severity::style, "useStlAlgorithm", "Consider using " + algoName + " algorithm instead of a raw loop.", CWE398, Certainty::normal); @@ -2841,7 +3002,7 @@ namespace { }; } // namespace -void CheckStl::useStlAlgorithm() +void CheckStlImpl::useStlAlgorithm() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -3042,7 +3203,7 @@ void CheckStl::useStlAlgorithm() } } -void CheckStl::knownEmptyContainerError(const Token *tok, const std::string& algo) +void CheckStlImpl::knownEmptyContainerError(const Token *tok, const std::string& algo) { const std::string var = tok ? tok->expressionString() : std::string("var"); @@ -3073,7 +3234,7 @@ static bool isKnownEmptyContainer(const Token* tok) }); } -void CheckStl::knownEmptyContainer() +void CheckStlImpl::knownEmptyContainer() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -3137,21 +3298,21 @@ static bool isLocalMutex(const Variable* var, const Scope* scope) return !var->isReference() && !var->isRValueReference() && !var->isStatic() && var->scope() == scope; } -void CheckStl::globalLockGuardError(const Token* tok) +void CheckStlImpl::globalLockGuardError(const Token* tok) { reportError(tok, Severity::warning, "globalLockGuard", "Lock guard is defined globally. Lock guards are intended to be local. A global lock guard could lead to a deadlock since it won't unlock until the end of the program.", CWE833, Certainty::normal); } -void CheckStl::localMutexError(const Token* tok) +void CheckStlImpl::localMutexError(const Token* tok) { reportError(tok, Severity::warning, "localMutex", "The lock is ineffective because the mutex is locked at the same scope as the mutex itself.", CWE667, Certainty::normal); } -void CheckStl::checkMutexes() +void CheckStlImpl::checkMutexes() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -3188,3 +3349,77 @@ void CheckStl::checkMutexes() } } +void CheckStl::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + if (!tokenizer.isCPP()) { + return; + } + + CheckStlImpl checkStl(&tokenizer, tokenizer.getSettings(), errorLogger); + checkStl.erase(); + checkStl.if_find(); + checkStl.checkFindInsert(); + checkStl.iterators(); + checkStl.missingComparison(); + checkStl.outOfBounds(); + checkStl.outOfBoundsIndexExpression(); + checkStl.redundantCondition(); + checkStl.string_c_str(); + checkStl.uselessCalls(); + checkStl.useStlAlgorithm(); + + checkStl.stlOutOfBounds(); + checkStl.negativeIndex(); + + checkStl.invalidContainer(); + checkStl.mismatchingContainers(); + checkStl.mismatchingContainerIterator(); + checkStl.knownEmptyContainer(); + + checkStl.stlBoundaries(); + checkStl.checkDereferenceInvalidIterator(); + checkStl.checkDereferenceInvalidIterator2(); + checkStl.checkMutexes(); + + // Style check + checkStl.size(); +} + +void CheckStl::getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const { + ErrorPath errorPath; + CheckStlImpl c(nullptr, settings, errorLogger); + c.outOfBoundsError(nullptr, "container", nullptr, "x", nullptr); + c.invalidIteratorError(nullptr, "iterator"); + c.iteratorsError(nullptr, "container1", "container2"); + c.iteratorsError(nullptr, nullptr, "container0", "container1"); + c.iteratorsError(nullptr, nullptr, "container"); + c.invalidContainerLoopError(nullptr, nullptr, errorPath); + c.invalidContainerError(nullptr, nullptr, nullptr, errorPath); + c.mismatchingContainerIteratorError(nullptr, nullptr); + c.mismatchingContainersError(nullptr, nullptr); + c.mismatchingContainerExpressionError(nullptr, nullptr); + c.sameIteratorExpressionError(nullptr); + c.dereferenceErasedError(nullptr, nullptr, "iter", false); + c.stlOutOfBoundsError(nullptr, "i", "foo", false); + c.negativeIndexError(nullptr, ValueFlow::Value(-1)); + c.stlBoundariesError(nullptr); + c.if_findError(nullptr, false); + c.if_findError(nullptr, true); + c.checkFindInsertError(nullptr); + c.string_c_strError(nullptr); + c.string_c_strReturn(nullptr); + c.string_c_strParam(nullptr, 0); + c.string_c_strThrowError(nullptr); + c.sizeError(nullptr); + c.missingComparisonError(nullptr, nullptr); + c.redundantIfRemoveError(nullptr); + c.uselessCallsReturnValueError(nullptr, "str", "find"); + c.uselessCallsSwapError(nullptr, "str"); + c.uselessCallsSubstrError(nullptr, CheckStlImpl::SubstrErrorType::COPY); + c.uselessCallsEmptyError(nullptr); + c.uselessCallsRemoveError(nullptr, "remove"); + c.dereferenceInvalidIteratorError(nullptr, "i"); + c.useStlAlgorithmError(nullptr, emptyString); + c.knownEmptyContainerError(nullptr, emptyString); + c.globalLockGuardError(nullptr); + c.localMutexError(nullptr); +} diff --git a/lib/checkstl.h b/lib/checkstl.h index d518d2d4dd5..e01ad4aa969 100644 --- a/lib/checkstl.h +++ b/lib/checkstl.h @@ -24,18 +24,12 @@ #include "check.h" #include "config.h" -#include "errortypes.h" -#include "tokenize.h" -#include "vfvalue.h" #include -class Scope; class Settings; -class Token; -class Variable; class ErrorLogger; - +class Tokenizer; /// @addtogroup Checks /// @{ @@ -45,241 +39,13 @@ class ErrorLogger; class CPPCHECKLIB CheckStl : public Check { public: /** This constructor is used when registering the CheckClass */ - CheckStl() : Check(myName()) {} + CheckStl() : Check("STL usage") {} private: - /** This constructor is used when running checks. */ - CheckStl(const Tokenizer* tokenizer, const Settings* settings, ErrorLogger* errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** run checks, the token list is not simplified */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - if (!tokenizer.isCPP()) { - return; - } - - CheckStl checkStl(&tokenizer, tokenizer.getSettings(), errorLogger); - checkStl.erase(); - checkStl.if_find(); - checkStl.checkFindInsert(); - checkStl.iterators(); - checkStl.missingComparison(); - checkStl.outOfBounds(); - checkStl.outOfBoundsIndexExpression(); - checkStl.redundantCondition(); - checkStl.string_c_str(); - checkStl.uselessCalls(); - checkStl.useStlAlgorithm(); - - checkStl.stlOutOfBounds(); - checkStl.negativeIndex(); - - checkStl.invalidContainer(); - checkStl.mismatchingContainers(); - checkStl.mismatchingContainerIterator(); - checkStl.knownEmptyContainer(); - - checkStl.stlBoundaries(); - checkStl.checkDereferenceInvalidIterator(); - checkStl.checkDereferenceInvalidIterator2(); - checkStl.checkMutexes(); - - // Style check - checkStl.size(); - } - - /** Accessing container out of bounds using ValueFlow */ - void outOfBounds(); - - /** Accessing container out of bounds, following index expression */ - void outOfBoundsIndexExpression(); - - /** - * Finds errors like this: - * for (unsigned ii = 0; ii <= foo.size(); ++ii) - */ - void stlOutOfBounds(); - - /** - * negative index for array like containers - */ - void negativeIndex(); - - /** - * Finds errors like this: - * for (it = foo.begin(); it != bar.end(); ++it) - */ - void iterators(); - - void invalidContainer(); - - bool checkIteratorPair(const Token* tok1, const Token* tok2); - - /** - * Mismatching containers: - * std::find(foo.begin(), bar.end(), x) - */ - void mismatchingContainers(); - - void mismatchingContainerIterator(); - - /** - * Dangerous usage of erase. The iterator is invalidated by erase so - * it is bad to dereference it after the erase. - */ - void erase(); - void eraseCheckLoopVar(const Scope& scope, const Variable* var); - - /** - * bad condition.. "it < alist.end()" - */ - void stlBoundaries(); - - /** if (a.find(x)) - possibly incorrect condition */ - void if_find(); - - void checkFindInsert(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** - * Suggest using empty() instead of checking size() against zero for containers. - * Item 4 from Scott Meyers book "Effective STL". - */ - void size(); - - /** - * Check for redundant condition 'if (ints.find(1) != ints.end()) ints.remove(123);' - * */ - void redundantCondition(); - - /** - * @brief Missing inner comparison, when incrementing iterator inside loop - * Dangers: - * - may increment iterator beyond end - * - may unintentionally skip elements in list/set etc - */ - void missingComparison(); - - /** Check for common mistakes when using the function string::c_str() */ - void string_c_str(); - - /** @brief %Check calls that using them is useless */ - void uselessCalls(); - - /** @brief %Check for dereferencing an iterator that is invalid */ - void checkDereferenceInvalidIterator(); - void checkDereferenceInvalidIterator2(); - - /** - * Dereferencing an erased iterator - * @param erased token where the erase occurs - * @param deref token where the dereference occurs - * @param itername iterator name - * @param inconclusive inconclusive flag - */ - void dereferenceErasedError(const Token* erased, const Token* deref, const std::string& itername, bool inconclusive); - - /** @brief Look for loops that can replaced with std algorithms */ - void useStlAlgorithm(); - - void knownEmptyContainer(); - - void checkMutexes(); - - bool isContainerSize(const Token *containerToken, const Token *expr) const; - bool isContainerSizeGE(const Token * containerToken, const Token *expr) const; - - void missingComparisonError(const Token* incrementToken1, const Token* incrementToken2); - void string_c_strThrowError(const Token* tok); - void string_c_strError(const Token* tok); - void string_c_strReturn(const Token* tok); - void string_c_strParam(const Token* tok, nonneg int number, const std::string& argtype = "std::string"); - void string_c_strConstructor(const Token* tok, const std::string& argtype = "std::string"); - void string_c_strAssignment(const Token* tok, const std::string& argtype = "std::string"); - void string_c_strConcat(const Token* tok); - void string_c_strStream(const Token* tok); - - void outOfBoundsError(const Token *tok, const std::string &containerName, const ValueFlow::Value *containerSize, const std::string &index, const ValueFlow::Value *indexValue); - void outOfBoundsIndexExpressionError(const Token *tok, const Token *index); - void stlOutOfBoundsError(const Token* tok, const std::string& num, const std::string& var, bool at); - void negativeIndexError(const Token* tok, const ValueFlow::Value& index); - void invalidIteratorError(const Token* tok, const std::string& iteratorName); - void iteratorsError(const Token* tok, const std::string& containerName1, const std::string& containerName2); - void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName1, const std::string& containerName2); - void iteratorsError(const Token* tok, const Token* containerTok, const std::string& containerName); - void mismatchingContainerIteratorError(const Token* tok, const Token* iterTok); - void mismatchingContainersError(const Token* tok1, const Token* tok2); - void mismatchingContainerExpressionError(const Token *tok1, const Token *tok2); - void sameIteratorExpressionError(const Token *tok); - void stlBoundariesError(const Token* tok); - void if_findError(const Token* tok, bool str); - void checkFindInsertError(const Token *tok); - void sizeError(const Token* tok); - void redundantIfRemoveError(const Token* tok); - void invalidContainerLoopError(const Token* tok, const Token* loopTok, ErrorPath errorPath); - void invalidContainerError(const Token *tok, const Token * contTok, const ValueFlow::Value *val, ErrorPath errorPath); - void invalidContainerReferenceError(const Token* tok, const Token* contTok, ErrorPath errorPath); - - void uselessCallsReturnValueError(const Token* tok, const std::string& varname, const std::string& function); - void uselessCallsSwapError(const Token* tok, const std::string& varname); - enum class SubstrErrorType { EMPTY, COPY, PREFIX, PREFIX_CONCAT }; - void uselessCallsSubstrError(const Token* tok, SubstrErrorType type); - void uselessCallsEmptyError(const Token* tok); - void uselessCallsRemoveError(const Token* tok, const std::string& function); - void uselessCallsConstructorError(const Token* tok); - - void dereferenceInvalidIteratorError(const Token* deref, const std::string& iterName); - void dereferenceInvalidIteratorError(const Token* tok, const ValueFlow::Value *value, bool inconclusive); - - void useStlAlgorithmError(const Token *tok, const std::string &algoName); - - void knownEmptyContainerError(const Token *tok, const std::string& algo); - - void globalLockGuardError(const Token *tok); - void localMutexError(const Token *tok); - - void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override { - ErrorPath errorPath; - CheckStl c(nullptr, settings, errorLogger); - c.outOfBoundsError(nullptr, "container", nullptr, "x", nullptr); - c.invalidIteratorError(nullptr, "iterator"); - c.iteratorsError(nullptr, "container1", "container2"); - c.iteratorsError(nullptr, nullptr, "container0", "container1"); - c.iteratorsError(nullptr, nullptr, "container"); - c.invalidContainerLoopError(nullptr, nullptr, errorPath); - c.invalidContainerError(nullptr, nullptr, nullptr, errorPath); - c.mismatchingContainerIteratorError(nullptr, nullptr); - c.mismatchingContainersError(nullptr, nullptr); - c.mismatchingContainerExpressionError(nullptr, nullptr); - c.sameIteratorExpressionError(nullptr); - c.dereferenceErasedError(nullptr, nullptr, "iter", false); - c.stlOutOfBoundsError(nullptr, "i", "foo", false); - c.negativeIndexError(nullptr, ValueFlow::Value(-1)); - c.stlBoundariesError(nullptr); - c.if_findError(nullptr, false); - c.if_findError(nullptr, true); - c.checkFindInsertError(nullptr); - c.string_c_strError(nullptr); - c.string_c_strReturn(nullptr); - c.string_c_strParam(nullptr, 0); - c.string_c_strThrowError(nullptr); - c.sizeError(nullptr); - c.missingComparisonError(nullptr, nullptr); - c.redundantIfRemoveError(nullptr); - c.uselessCallsReturnValueError(nullptr, "str", "find"); - c.uselessCallsSwapError(nullptr, "str"); - c.uselessCallsSubstrError(nullptr, SubstrErrorType::COPY); - c.uselessCallsEmptyError(nullptr); - c.uselessCallsRemoveError(nullptr, "remove"); - c.dereferenceInvalidIteratorError(nullptr, "i"); - c.useStlAlgorithmError(nullptr, emptyString); - c.knownEmptyContainerError(nullptr, emptyString); - c.globalLockGuardError(nullptr); - c.localMutexError(nullptr); - } - - static std::string myName() { - return "STL usage"; - } + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override; std::string classInfo() const override { return "Check for invalid usage of STL:\n" diff --git a/lib/checkstring.cpp b/lib/checkstring.cpp index 5dc52d74c97..480c5d6ad4a 100644 --- a/lib/checkstring.cpp +++ b/lib/checkstring.cpp @@ -21,6 +21,7 @@ #include "checkstring.h" #include "astutils.h" +#include "checkimpl.h" #include "errortypes.h" #include "mathlib.h" #include "settings.h" @@ -49,10 +50,51 @@ static const CWE CWE628(628U); // Function Call with Incorrectly Specified Arg static const CWE CWE665(665U); // Improper Initialization static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior +namespace { + class CheckStringImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckStringImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief undefined behaviour, writing string literal */ + void stringLiteralWrite(); + + /** @brief str plus char (unusual pointer arithmetic) */ + void strPlusChar(); + + /** @brief %Check for using bad usage of strncmp and substr */ + void checkIncorrectStringCompare(); + + /** @brief %Check for comparison of a string literal with a char* variable */ + void checkSuspiciousStringCompare(); + + /** @brief %Check for suspicious code that compares string literals for equality */ + void checkAlwaysTrueOrFalseStringCompare(); + + /** @brief %Check for overlapping strcmp() */ + void overlappingStrcmp(); + + /** @brief %Check for overlapping source and destination passed to sprintf() */ + void sprintfOverlappingData(); + + void stringLiteralWriteError(const Token *tok, const Token *strValue); + void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname); + void strPlusCharError(const Token *tok); + void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); + void incorrectStringBooleanError(const Token *tok, const std::string& string); + void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); + void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); + void suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong); + void suspiciousStringCompareError_char(const Token* tok, const std::string& var); + void overlappingStrcmpError(const Token* eq0, const Token *ne0); + }; +} + //--------------------------------------------------------------------------- // Writing string literal is UB //--------------------------------------------------------------------------- -void CheckString::stringLiteralWrite() +void CheckStringImpl::stringLiteralWrite() { logChecker("CheckString::stringLiteralWrite"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -71,7 +113,7 @@ void CheckString::stringLiteralWrite() } } -void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValue) +void CheckStringImpl::stringLiteralWriteError(const Token *tok, const Token *strValue) { std::list callstack{ tok }; if (strValue) @@ -94,7 +136,7 @@ void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValu // Check for string comparison involving two static strings. // if(strcmp("00FF00","00FF00")==0) // <- statement is always true //--------------------------------------------------------------------------- -void CheckString::checkAlwaysTrueOrFalseStringCompare() +void CheckStringImpl::checkAlwaysTrueOrFalseStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -138,7 +180,7 @@ void CheckString::checkAlwaysTrueOrFalseStringCompare() } } -void CheckString::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) +void CheckStringImpl::alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2) { constexpr std::size_t stringLen = 10; const std::string string1 = (str1.size() < stringLen) ? str1 : (str1.substr(0, stringLen-2) + ".."); @@ -150,7 +192,7 @@ void CheckString::alwaysTrueFalseStringCompareError(const Token *tok, const std: "Therefore the comparison is unnecessary and looks suspicious.", (str1==str2)?CWE571:CWE570, Certainty::normal); } -void CheckString::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) +void CheckStringImpl::alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2) { reportError(tok, Severity::warning, "stringCompare", "Comparison of identical string variables.\n" @@ -163,7 +205,7 @@ void CheckString::alwaysTrueStringVariableCompareError(const Token *tok, const s // Detect "str == '\0'" where "*str == '\0'" is correct. // Comparing char* with each other instead of using strcmp() //----------------------------------------------------------------------------- -void CheckString::checkSuspiciousStringCompare() +void CheckStringImpl::checkSuspiciousStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -202,14 +244,14 @@ void CheckString::checkSuspiciousStringCompare() } } -void CheckString::suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong) +void CheckStringImpl::suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong) { const std::string cmpFunc = isLong ? "wcscmp" : "strcmp"; reportError(tok, Severity::warning, "literalWithCharPtrCompare", "$symbol:" + var + "\nString literal compared with variable '$symbol'. Did you intend to use " + cmpFunc + "() instead?", CWE595, Certainty::normal); } -void CheckString::suspiciousStringCompareError_char(const Token* tok, const std::string& var) +void CheckStringImpl::suspiciousStringCompareError_char(const Token* tok, const std::string& var) { reportError(tok, Severity::warning, "charLiteralWithCharPtrCompare", "$symbol:" + var + "\nChar literal compared with pointer '$symbol'. Did you intend to dereference it?", CWE595, Certainty::normal); @@ -225,7 +267,7 @@ static bool isChar(const Variable* var) return (var && !var->isPointer() && !var->isArray() && (var->typeStartToken()->str() == "char" || var->typeStartToken()->str() == "wchar_t")); } -void CheckString::strPlusChar() +void CheckStringImpl::strPlusChar() { logChecker("CheckString::strPlusChar"); const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase(); @@ -241,7 +283,7 @@ void CheckString::strPlusChar() } } -void CheckString::strPlusCharError(const Token *tok) +void CheckStringImpl::strPlusCharError(const Token *tok) { std::string charType = "char"; if (tok && tok->astOperand2() && tok->astOperand2()->variable()) @@ -274,7 +316,7 @@ static bool isMacroUsage(const Token* tok) // Implicit casts of string literals to bool // Comparing string literal with strlen() with wrong length //--------------------------------------------------------------------------- -void CheckString::checkIncorrectStringCompare() +void CheckStringImpl::checkIncorrectStringCompare() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -322,12 +364,12 @@ void CheckString::checkIncorrectStringCompare() } } -void CheckString::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) +void CheckStringImpl::incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string) { reportError(tok, Severity::warning, "incorrectStringCompare", "$symbol:" + func + "\nString literal " + string + " doesn't match length argument for $symbol().", CWE570, Certainty::normal); } -void CheckString::incorrectStringBooleanError(const Token *tok, const std::string& string) +void CheckStringImpl::incorrectStringBooleanError(const Token *tok, const std::string& string) { const bool charLiteral = isCharLiteral(string); const std::string literalType = charLiteral ? "char" : "string"; @@ -342,7 +384,7 @@ void CheckString::incorrectStringBooleanError(const Token *tok, const std::strin // always true: strcmp(str,"a")==0 || strcmp(str,"b") // TODO: Library configuration for string comparison functions //--------------------------------------------------------------------------- -void CheckString::overlappingStrcmp() +void CheckStringImpl::overlappingStrcmp() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -404,7 +446,7 @@ void CheckString::overlappingStrcmp() } } -void CheckString::overlappingStrcmpError(const Token *eq0, const Token *ne0) +void CheckStringImpl::overlappingStrcmpError(const Token *eq0, const Token *ne0) { std::string eq0Expr(eq0 ? eq0->expressionString() : std::string("strcmp(x,\"abc\")")); if (eq0 && eq0->astParent()->str() == "!") @@ -421,7 +463,7 @@ void CheckString::overlappingStrcmpError(const Token *eq0, const Token *ne0) // Overlapping source and destination passed to sprintf(). // TODO: Library configuration for overlapping arguments //--------------------------------------------------------------------------- -void CheckString::sprintfOverlappingData() +void CheckStringImpl::sprintfOverlappingData() { logChecker("CheckString::sprintfOverlappingData"); @@ -459,7 +501,7 @@ void CheckString::sprintfOverlappingData() } } -void CheckString::sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname) +void CheckStringImpl::sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname) { const std::string func = funcTok ? funcTok->str() : "s[n]printf"; @@ -472,3 +514,31 @@ void CheckString::sprintfOverlappingDataError(const Token *funcTok, const Token "\"If copying takes place between objects that overlap as a result of a call " "to sprintf() or snprintf(), the results are undefined.\"", CWE628, Certainty::normal); } + +void CheckString::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckStringImpl checkString(&tokenizer, tokenizer.getSettings(), errorLogger); + + // Checks + checkString.strPlusChar(); + checkString.checkSuspiciousStringCompare(); + checkString.stringLiteralWrite(); + checkString.overlappingStrcmp(); + checkString.checkIncorrectStringCompare(); + checkString.sprintfOverlappingData(); + checkString.checkAlwaysTrueOrFalseStringCompare(); +} + +void CheckString::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckStringImpl c(nullptr, settings, errorLogger); + c.stringLiteralWriteError(nullptr, nullptr); + c.sprintfOverlappingDataError(nullptr, nullptr, "varname"); + c.strPlusCharError(nullptr); + c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); + c.suspiciousStringCompareError(nullptr, "foo", false); + c.suspiciousStringCompareError_char(nullptr, "foo"); + c.incorrectStringBooleanError(nullptr, "\"Hello World\""); + c.incorrectStringBooleanError(nullptr, "\'x\'"); + c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); + c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); + c.overlappingStrcmpError(nullptr, nullptr); +} diff --git a/lib/checkstring.h b/lib/checkstring.h index c9119243003..86f29d41c2b 100644 --- a/lib/checkstring.h +++ b/lib/checkstring.h @@ -24,13 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -41,77 +40,13 @@ class Token; class CPPCHECKLIB CheckString : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckString() : Check(myName()) {} + CheckString() : Check("String") {} private: - /** @brief This constructor is used when running checks. */ - CheckString(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckString checkString(&tokenizer, tokenizer.getSettings(), errorLogger); - - // Checks - checkString.strPlusChar(); - checkString.checkSuspiciousStringCompare(); - checkString.stringLiteralWrite(); - checkString.overlappingStrcmp(); - checkString.checkIncorrectStringCompare(); - checkString.sprintfOverlappingData(); - checkString.checkAlwaysTrueOrFalseStringCompare(); - } - - /** @brief undefined behaviour, writing string literal */ - void stringLiteralWrite(); - - /** @brief str plus char (unusual pointer arithmetic) */ - void strPlusChar(); - - /** @brief %Check for using bad usage of strncmp and substr */ - void checkIncorrectStringCompare(); - - /** @brief %Check for comparison of a string literal with a char* variable */ - void checkSuspiciousStringCompare(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check for suspicious code that compares string literals for equality */ - void checkAlwaysTrueOrFalseStringCompare(); - - /** @brief %Check for overlapping strcmp() */ - void overlappingStrcmp(); - - /** @brief %Check for overlapping source and destination passed to sprintf() */ - void sprintfOverlappingData(); - - void stringLiteralWriteError(const Token *tok, const Token *strValue); - void sprintfOverlappingDataError(const Token *funcTok, const Token *tok, const std::string &varname); - void strPlusCharError(const Token *tok); - void incorrectStringCompareError(const Token *tok, const std::string& func, const std::string &string); - void incorrectStringBooleanError(const Token *tok, const std::string& string); - void alwaysTrueFalseStringCompareError(const Token *tok, const std::string& str1, const std::string& str2); - void alwaysTrueStringVariableCompareError(const Token *tok, const std::string& str1, const std::string& str2); - void suspiciousStringCompareError(const Token* tok, const std::string& var, bool isLong); - void suspiciousStringCompareError_char(const Token* tok, const std::string& var); - void overlappingStrcmpError(const Token* eq0, const Token *ne0); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckString c(nullptr, settings, errorLogger); - c.stringLiteralWriteError(nullptr, nullptr); - c.sprintfOverlappingDataError(nullptr, nullptr, "varname"); - c.strPlusCharError(nullptr); - c.incorrectStringCompareError(nullptr, "substr", "\"Hello World\""); - c.suspiciousStringCompareError(nullptr, "foo", false); - c.suspiciousStringCompareError_char(nullptr, "foo"); - c.incorrectStringBooleanError(nullptr, "\"Hello World\""); - c.incorrectStringBooleanError(nullptr, "\'x\'"); - c.alwaysTrueFalseStringCompareError(nullptr, "str1", "str2"); - c.alwaysTrueStringVariableCompareError(nullptr, "varname1", "varname2"); - c.overlappingStrcmpError(nullptr, nullptr); - } - - static std::string myName() { - return "String"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Detect misusage of C-style strings:\n" diff --git a/lib/checktype.cpp b/lib/checktype.cpp index b8a3d26d448..ec24116e743 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -20,6 +20,7 @@ //--------------------------------------------------------------------------- #include "checktype.h" +#include "checkimpl.h" #include "errortypes.h" #include "mathlib.h" #include "platform.h" @@ -57,8 +58,50 @@ static const CWE CWE197(197U); // Numeric Truncation Error static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior static const CWE CWE190(190U); // Integer Overflow or Wraparound +namespace { + class CheckTypeImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckTypeImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for bitwise shift with too big right operand */ + void checkTooBigBitwiseShift(); + + /** @brief %Check for integer overflow */ + void checkIntegerOverflow(); + + /** @brief %Check for dangerous sign conversion */ + void checkSignConversion(); + + /** @brief %Check for implicit long cast of int result */ + void checkLongCast(); + + /** @brief %Check for float to integer overflow */ + void checkFloatToIntegerOverflow(); + void checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues); + + // Error messages.. + void tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); + void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); + void integerOverflowError(const Token *tok, const ValueFlow::Value &value); + void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); + void longCastAssignError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); + void longCastReturnError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); + void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); + }; +} -void CheckType::checkTooBigBitwiseShift() +static std::string getMessageId(const ValueFlow::Value &value, const char id[]) +{ + if (value.condition != nullptr) + return id + std::string("Cond"); + if (value.safe) + return std::string("safe") + (char)std::toupper(id[0]) + (id + 1); + return id; +} + +void CheckTypeImpl::checkTooBigBitwiseShift() { // unknown sizeof(int) => can't run this checker if (mSettings->platform.type == Platform::Type::Unspecified) @@ -110,7 +153,7 @@ void CheckType::checkTooBigBitwiseShift() } } -void CheckType::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) +void CheckTypeImpl::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { constexpr char id[] = "shiftTooManyBits"; @@ -129,7 +172,7 @@ void CheckType::tooBigBitwiseShiftError(const Token *tok, int lhsbits, const Val reportError(errorPath, rhsbits.errorSeverity() ? Severity::error : Severity::warning, id, errmsg.str(), CWE758, rhsbits.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } -void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) +void CheckTypeImpl::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits) { constexpr char id[] = "shiftTooManyBitsSigned"; @@ -164,7 +207,7 @@ void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, con // Checking for integer overflow //--------------------------------------------------------------------------- -void CheckType::checkIntegerOverflow() +void CheckTypeImpl::checkIntegerOverflow() { // unknown sizeof(int) => can't run this checker if (mSettings->platform.type == Platform::Type::Unspecified || mSettings->platform.int_bit >= MathLib::bigint_bits) @@ -212,7 +255,7 @@ void CheckType::checkIntegerOverflow() } } -void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &value) +void CheckTypeImpl::integerOverflowError(const Token *tok, const ValueFlow::Value &value) { const std::string expr(tok ? tok->expressionString() : ""); @@ -238,7 +281,7 @@ void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &v // Checking for sign conversion when operand can be negative //--------------------------------------------------------------------------- -void CheckType::checkSignConversion() +void CheckTypeImpl::checkSignConversion() { if (!mSettings->severity.isEnabled(Severity::warning)) return; @@ -270,7 +313,7 @@ void CheckType::checkSignConversion() } } -void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue) +void CheckTypeImpl::signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue) { const std::string expr(tok ? tok->expressionString() : "var"); @@ -288,7 +331,7 @@ void CheckType::signConversionError(const Token *tok, const ValueFlow::Value *ne const ErrorPath &errorPath = getErrorPath(tok,negativeValue,"Negative value is converted to an unsigned value"); reportError(errorPath, Severity::warning, - Check::getMessageId(*negativeValue, "signConversion").c_str(), + getMessageId(*negativeValue, "signConversion").c_str(), msg.str(), CWE195, negativeValue->isInconclusive() ? Certainty::inconclusive : Certainty::normal); @@ -320,7 +363,7 @@ static bool checkTypeCombination(const ValueType& src, const ValueType& tgt, con }); } -void CheckType::checkLongCast() +void CheckTypeImpl::checkLongCast() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -395,7 +438,7 @@ static void makeBaseTypeString(std::string& typeStr) typeStr.erase(typeStr.begin(), typeStr.begin() + pos + 6 + 1); } -void CheckType::longCastAssignError(const Token *tok, const ValueType* src, const ValueType* tgt) +void CheckTypeImpl::longCastAssignError(const Token *tok, const ValueType* src, const ValueType* tgt) { std::string srcStr = src ? src->str() : "int"; makeBaseTypeString(srcStr); @@ -408,7 +451,7 @@ void CheckType::longCastAssignError(const Token *tok, const ValueType* src, cons srcStr + " result is assigned to " + tgtStr + " variable. If the variable is " + tgtStr + " to avoid loss of information, then there is loss of information. To avoid loss of information you must cast a calculation operand to " + tgtStr + ", for example 'l = a * b;' => 'l = (" + tgtStr + ")a * b;'.", CWE197, Certainty::normal); } -void CheckType::longCastReturnError(const Token *tok, const ValueType* src, const ValueType* tgt) +void CheckTypeImpl::longCastReturnError(const Token *tok, const ValueType* src, const ValueType* tgt) { std::string srcStr = src ? src->str() : "int"; makeBaseTypeString(srcStr); @@ -425,7 +468,7 @@ void CheckType::longCastReturnError(const Token *tok, const ValueType* src, cons // Checking for float to integer overflow //--------------------------------------------------------------------------- -void CheckType::checkFloatToIntegerOverflow() +void CheckTypeImpl::checkFloatToIntegerOverflow() { logChecker("CheckType::checkFloatToIntegerOverflow"); @@ -459,7 +502,7 @@ void CheckType::checkFloatToIntegerOverflow() } } -void CheckType::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues) +void CheckTypeImpl::checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues) { // Conversion of float to integer? if (!vtint || !vtint->isIntegral()) @@ -496,7 +539,7 @@ void CheckType::checkFloatToIntegerOverflow(const Token *tok, const ValueType *v } } -void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) +void CheckTypeImpl::floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value) { std::ostringstream errmsg; errmsg << "Undefined behaviour: float (" << value.floatValue << ") to integer conversion overflow."; @@ -505,3 +548,27 @@ void CheckType::floatToIntegerOverflowError(const Token *tok, const ValueFlow::V "floatConversionOverflow", errmsg.str(), CWE190, value.isInconclusive() ? Certainty::inconclusive : Certainty::normal); } + +void CheckType::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + // These are not "simplified" because casts can't be ignored + CheckTypeImpl checkType(&tokenizer, tokenizer.getSettings(), errorLogger); + checkType.checkTooBigBitwiseShift(); + checkType.checkIntegerOverflow(); + checkType.checkSignConversion(); + checkType.checkLongCast(); + checkType.checkFloatToIntegerOverflow(); +} + +void CheckType::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckTypeImpl c(nullptr, settings, errorLogger); + c.tooBigBitwiseShiftError(nullptr, 32, ValueFlow::Value(64)); + c.tooBigSignedBitwiseShiftError(nullptr, 31, ValueFlow::Value(31)); + c.integerOverflowError(nullptr, ValueFlow::Value(1LL<<32)); + c.signConversionError(nullptr, nullptr, false); + c.longCastAssignError(nullptr); + c.longCastReturnError(nullptr); + ValueFlow::Value f; + f.valueType = ValueFlow::Value::ValueType::FLOAT; + f.floatValue = 1E100; + c.floatToIntegerOverflowError(nullptr, f); +} diff --git a/lib/checktype.h b/lib/checktype.h index ae99cffc45f..af23a3287a6 100644 --- a/lib/checktype.h +++ b/lib/checktype.h @@ -24,16 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" -#include "vfvalue.h" -#include #include class ErrorLogger; class Settings; -class Token; -class ValueType; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -44,66 +40,13 @@ class ValueType; class CPPCHECKLIB CheckType : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckType() : Check(myName()) {} + CheckType() : Check("Type") {} private: - /** @brief This constructor is used when running checks. */ - CheckType(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - // These are not "simplified" because casts can't be ignored - CheckType checkType(&tokenizer, tokenizer.getSettings(), errorLogger); - checkType.checkTooBigBitwiseShift(); - checkType.checkIntegerOverflow(); - checkType.checkSignConversion(); - checkType.checkLongCast(); - checkType.checkFloatToIntegerOverflow(); - } - - /** @brief %Check for bitwise shift with too big right operand */ - void checkTooBigBitwiseShift(); - - /** @brief %Check for integer overflow */ - void checkIntegerOverflow(); - - /** @brief %Check for dangerous sign conversion */ - void checkSignConversion(); - - /** @brief %Check for implicit long cast of int result */ - void checkLongCast(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check for float to integer overflow */ - void checkFloatToIntegerOverflow(); - void checkFloatToIntegerOverflow(const Token *tok, const ValueType *vtint, const ValueType *vtfloat, const std::list &floatValues); - - // Error messages.. - void tooBigBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); - void tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, const ValueFlow::Value &rhsbits); - void integerOverflowError(const Token *tok, const ValueFlow::Value &value); - void signConversionError(const Token *tok, const ValueFlow::Value *negativeValue, const bool constvalue); - void longCastAssignError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); - void longCastReturnError(const Token *tok, const ValueType* src = nullptr, const ValueType* tgt = nullptr); - void floatToIntegerOverflowError(const Token *tok, const ValueFlow::Value &value); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckType c(nullptr, settings, errorLogger); - c.tooBigBitwiseShiftError(nullptr, 32, ValueFlow::Value(64)); - c.tooBigSignedBitwiseShiftError(nullptr, 31, ValueFlow::Value(31)); - c.integerOverflowError(nullptr, ValueFlow::Value(1LL<<32)); - c.signConversionError(nullptr, nullptr, false); - c.longCastAssignError(nullptr); - c.longCastReturnError(nullptr); - ValueFlow::Value f; - f.valueType = ValueFlow::Value::ValueType::FLOAT; - f.floatValue = 1E100; - c.floatToIntegerOverflowError(nullptr, f); - } - - static std::string myName() { - return "Type"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Type checks\n" diff --git a/lib/checkuninitvar.cpp b/lib/checkuninitvar.cpp index 94d64d27537..a5f5819400a 100644 --- a/lib/checkuninitvar.cpp +++ b/lib/checkuninitvar.cpp @@ -21,6 +21,7 @@ #include "checkuninitvar.h" #include "astutils.h" +#include "checkimpl.h" #include "ctu.h" #include "errorlogger.h" #include "library.h" @@ -59,6 +60,57 @@ namespace { //--------------------------------------------------------------------------- +namespace { + struct VariableValue { + explicit VariableValue(MathLib::bigint val = 0) : value(val) {} + MathLib::bigint value; + bool notEqual{}; + }; + + class CheckUninitVarImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckUninitVarImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + bool diag(const Token* tok); + /** Check for uninitialized variables */ + void check(); + void checkScope(const Scope* scope, const std::set &arrayTypeDefs); + void checkStruct(const Token *tok, const Variable &structvar); + bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, CheckUninitVar::Alloc* const alloc, const std::string &membervar, std::map& variableValue); + const Token* checkExpr(const Token* tok, const Variable& var, const CheckUninitVar::Alloc alloc, bool known, bool* bailout = nullptr) const; + bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, CheckUninitVar::Alloc alloc, const std::string &membervar); + bool checkLoopBody(const Token *tok, const Variable& var, const CheckUninitVar::Alloc alloc, const std::string &membervar, const bool suppressErrors); + const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const CheckUninitVar::Alloc alloc, const std::string &membervar, bool &bailout) const; + void checkRhs(const Token *tok, const Variable &var, CheckUninitVar::Alloc alloc, nonneg int number_of_if, const std::string &membervar); + static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, CheckUninitVar::Alloc alloc, int indirect = 0); + int isFunctionParUsage(const Token *vartok, bool pointer, CheckUninitVar::Alloc alloc, int indirect = 0) const; + bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const; + bool isMemberVariableUsage(const Token *tok, bool isPointer, CheckUninitVar::Alloc alloc, const std::string &membervar) const; + + /** ValueFlow-based checking for uninitialized variables */ + void valueFlowUninit(); + + void uninitvarError(const Token* tok, const ValueFlow::Value& v); + void uninitdataError(const Token *tok, const std::string &varname); + void uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath); + void uninitvarError(const Token *tok, const std::string &varname) { + ErrorPath errorPath; + uninitvarError(tok, varname, errorPath); + } + void uninitvarError(const Token *tok, const std::string &varname, CheckUninitVar::Alloc alloc) { + if (alloc == CheckUninitVar::NO_CTOR_CALL || alloc == CheckUninitVar::CTOR_CALL) + uninitdataError(tok, varname); + else + uninitvarError(tok, varname); + } + void uninitStructMemberError(const Token *tok, const std::string &membername); + + std::set mUninitDiags; + }; +} + // get ast parent, skip possible address-of and casts static const Token *getAstParentSkipPossibleCastAndAddressOf(const Token *vartok, bool *unknown) { @@ -104,7 +156,7 @@ static std::map getVariableValues(const Token* tok) { return ret; } -bool CheckUninitVar::diag(const Token* tok) +bool CheckUninitVarImpl::diag(const Token* tok) { if (!tok) return true; @@ -113,7 +165,7 @@ bool CheckUninitVar::diag(const Token* tok) return !mUninitDiags.insert(tok).second; } -void CheckUninitVar::check() +void CheckUninitVarImpl::check() { logChecker("CheckUninitVar::check"); @@ -133,7 +185,7 @@ void CheckUninitVar::check() } } -void CheckUninitVar::checkScope(const Scope* scope, const std::set &arrayTypeDefs) +void CheckUninitVarImpl::checkScope(const Scope* scope, const std::set &arrayTypeDefs) { for (const Variable &var : scope->varlist) { if ((mTokenizer->isCPP() && var.type() && !var.isPointer() && var.type()->needInitialization != Type::NeedInitialization::True) || @@ -148,11 +200,11 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set continue; if (Token::Match(var.nameToken(), "%name% =")) { // Variable is initialized, but Rhs might be not - checkRhs(var.nameToken(), var, NO_ALLOC, 0U, emptyString); + checkRhs(var.nameToken(), var, CheckUninitVar::NO_ALLOC, 0U, emptyString); continue; } if (Token::Match(var.nameToken(), "%name% ) (") && Token::simpleMatch(var.nameToken()->linkAt(2), ") =")) { // Function pointer is initialized, but Rhs might be not - checkRhs(var.nameToken()->linkAt(2)->next(), var, NO_ALLOC, 0U, emptyString); + checkRhs(var.nameToken()->linkAt(2)->next(), var, CheckUninitVar::NO_ALLOC, 0U, emptyString); continue; } @@ -184,7 +236,7 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set continue; if (tok->astParent() && Token::simpleMatch(tok->astParent()->previous(), "for (") && - checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? ARRAY : NO_ALLOC, emptyString, true)) + checkLoopBody(tok->astParent()->link()->next(), var, var.isArray() ? CheckUninitVar::ARRAY : CheckUninitVar::NO_ALLOC, emptyString, true)) continue; if (var.isArray()) { @@ -196,14 +248,14 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set } } if (!init) { - Alloc alloc = ARRAY; + CheckUninitVar::Alloc alloc = CheckUninitVar::ARRAY; std::map variableValue = getVariableValues(var.typeStartToken()); checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); } continue; } if (stdtype || var.isPointer()) { - Alloc alloc = NO_ALLOC; + CheckUninitVar::Alloc alloc = CheckUninitVar::NO_ALLOC; std::map variableValue = getVariableValues(var.typeStartToken()); checkScopeForVariable(tok, var, nullptr, nullptr, &alloc, emptyString, variableValue); } @@ -228,7 +280,7 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set 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; + CheckUninitVar::Alloc alloc = CheckUninitVar::NO_ALLOC; std::map variableValue; checkScopeForVariable(tok->next(), arg, nullptr, nullptr, &alloc, emptyString, variableValue); } @@ -238,7 +290,7 @@ void CheckUninitVar::checkScope(const Scope* scope, const std::set } } -void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) +void CheckUninitVarImpl::checkStruct(const Token *tok, const Variable &structvar) { const Token *typeToken = structvar.typeStartToken(); while (Token::Match(typeToken, "%name% ::")) @@ -264,7 +316,7 @@ void CheckUninitVar::checkStruct(const Token *tok, const Variable &structvar) } if (!innerunion) { - Alloc alloc = NO_ALLOC; + CheckUninitVar::Alloc alloc = CheckUninitVar::NO_ALLOC; const Token *tok2 = tok; if (tok->str() == "}") tok2 = tok2->next(); @@ -399,7 +451,7 @@ static bool isVariableUsed(const Token *tok, const Variable& var) return !parent2 || parent2->isConstOp() || (parent2->str() == "=" && parent2->astOperand2() == parent); } -bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, Alloc* const alloc, const std::string &membervar, std::map& variableValue) +bool CheckUninitVarImpl::checkScopeForVariable(const Token *tok, const Variable& var, bool * const possibleInit, bool * const noreturn, CheckUninitVar::Alloc* const alloc, const std::string &membervar, std::map& variableValue) { const bool suppressErrors(possibleInit && *possibleInit); // Assume that this is a variable declaration, rather than a fundef const bool printDebug = mSettings->debugwarnings; @@ -735,7 +787,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var } // Use variable - else if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) + else if (!suppressErrors && CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), *alloc)) uninitvarError(tok, tok->str(), *alloc); return true; @@ -771,13 +823,13 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var if (rhs && Token::Match(rhs->previous(), "%name% (")) { const Library::AllocFunc *allocFunc = mSettings->library.getAllocFuncInfo(rhs->astOperand1()); if (allocFunc && !allocFunc->initData) { - *alloc = NO_CTOR_CALL; + *alloc = CheckUninitVar::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")) { - *alloc = CTOR_CALL; + *alloc = CheckUninitVar::CTOR_CALL; // type has constructor(s) if (var.typeScope() && var.typeScope()->numConstructors > 0) @@ -817,7 +869,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var } else { // Use variable - if (!suppressErrors && isVariableUsage(tok, var.isPointer(), *alloc)) { + if (!suppressErrors && CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), *alloc)) { uninitvarError(tok, tok->str(), *alloc); return true; } @@ -851,7 +903,7 @@ bool CheckUninitVar::checkScopeForVariable(const Token *tok, const Variable& var return false; } -const Token* CheckUninitVar::checkExpr(const Token* tok, const Variable& var, const Alloc alloc, bool known, bool* bailout) const +const Token* CheckUninitVarImpl::checkExpr(const Token* tok, const Variable& var, const CheckUninitVar::Alloc alloc, bool known, bool* bailout) const { if (!tok) return nullptr; @@ -871,7 +923,7 @@ const Token* CheckUninitVar::checkExpr(const Token* tok, const Variable& var, co if (tok->astOperand2()) return checkExpr(tok->astOperand2(), var, alloc, known, bailout); if (tok->varId() == var.declarationId()) { - const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc); + const Token *errorToken = CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), alloc); if (errorToken) return errorToken; if (bailout) @@ -880,7 +932,7 @@ const Token* CheckUninitVar::checkExpr(const Token* tok, const Variable& var, co return nullptr; } -bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar) +bool CheckUninitVarImpl::checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, CheckUninitVar::Alloc alloc, const std::string &membervar) { const Token * const endpar = startparentheses->link(); if (Token::Match(startparentheses, "( ! %name% %oror%") && startparentheses->tokAt(2)->getValue(0)) @@ -900,7 +952,7 @@ bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Va continue; } - if (const Token *errorToken = isVariableUsage(tok, var.isPointer(), alloc)) { + if (const Token *errorToken = CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), alloc)) { if (suppressErrors) continue; uninitvarError(errorToken, errorToken->expressionString(), alloc); @@ -917,7 +969,7 @@ bool CheckUninitVar::checkIfForWhileHead(const Token *startparentheses, const Va } /** recursively check loop, return error token */ -const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const +const Token* CheckUninitVarImpl::checkLoopBodyRecursive(const Token *start, const Variable& var, const CheckUninitVar::Alloc alloc, const std::string &membervar, bool &bailout) const { assert(start->str() == "{"); @@ -1034,7 +1086,7 @@ const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Va return nullptr; } } else { - if (const Token *errtok = isVariableUsage(tok, var.isPointer(), alloc)) { + if (const Token *errtok = CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), alloc)) { if (!conditionalUsage) return errtok; if (!errorToken) @@ -1066,7 +1118,7 @@ const Token* CheckUninitVar::checkLoopBodyRecursive(const Token *start, const Va return errorToken; } -bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors) +bool CheckUninitVarImpl::checkLoopBody(const Token *tok, const Variable& var, const CheckUninitVar::Alloc alloc, const std::string &membervar, const bool suppressErrors) { bool bailout = false; const Token *errorToken = checkLoopBodyRecursive(tok, var, alloc, membervar, bailout); @@ -1082,7 +1134,7 @@ bool CheckUninitVar::checkLoopBody(const Token *tok, const Variable& var, const return bailout; } -void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar) +void CheckUninitVarImpl::checkRhs(const Token *tok, const Variable &var, CheckUninitVar::Alloc alloc, nonneg int number_of_if, const std::string &membervar) { bool rhs = false; int indent = 0; @@ -1090,7 +1142,7 @@ void CheckUninitVar::checkRhs(const Token *tok, const Variable &var, Alloc alloc if (tok->str() == "=") rhs = true; else if (rhs && tok->varId() == var.declarationId()) { - if (membervar.empty() && isVariableUsage(tok, var.isPointer(), alloc)) + if (membervar.empty() && CheckUninitVar::isVariableUsage(tok, mSettings->library, var.isPointer(), alloc)) uninitvarError(tok, tok->str(), alloc); else if (!membervar.empty() && isMemberVariableUsage(tok, var.isPointer(), alloc, membervar)) uninitStructMemberError(tok, tok->str() + "." + membervar); @@ -1134,7 +1186,7 @@ static bool isVoidCast(const Token *tok) return Token::simpleMatch(tok, "(") && tok->isCast() && tok->valueType() && tok->valueType()->type == ValueType::Type::VOID && tok->valueType()->pointer == 0; } -const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) +const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& library, bool pointer, CheckUninitVar::Alloc alloc, int indirect) { const bool cpp = vartok->isCpp(); const Token *valueExpr = vartok; // non-dereferenced , no address of value as variable @@ -1162,7 +1214,7 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& valueExpr = valueExpr->astParent(); } const Token *derefValue = nullptr; // dereferenced value expression - if (alloc != NO_ALLOC) { + if (alloc != CheckUninitVar::NO_ALLOC) { const int arrayDim = (vartok->variable() && vartok->variable()->isArray()) ? vartok->variable()->dimensions().size() : 1; int deref = 0; derefValue = valueExpr; @@ -1225,12 +1277,12 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& if (isVoidCast(parent)) return nullptr; } - if (alloc != NO_ALLOC) { + if (alloc != CheckUninitVar::NO_ALLOC) { if (Token::Match(valueExpr->astParent(), "%comp%|%oror%|&&|?|!")) return nullptr; if (Token::Match(valueExpr->astParent(), "%or%|&") && valueExpr->astParent()->isBinaryOp()) return nullptr; - if (alloc == CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue)) + if (alloc == CheckUninitVar::CTOR_CALL && derefValue && Token::simpleMatch(derefValue->astParent(), "(") && astIsLhs(derefValue)) return nullptr; if (Token::simpleMatch(valueExpr->astParent(), "return")) return nullptr; @@ -1243,17 +1295,17 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& parent = parent->astParent(); if (Token::simpleMatch(parent, "{")) return valueExpr; - const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); + const int use = CheckUninitVarImpl::isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); return (use>0) ? valueExpr : nullptr; } if (derefValue && Token::Match(derefValue->astParent(), "[(,]") && (derefValue->astParent()->str() == "," || astIsRhs(derefValue))) { - const int use = isFunctionParUsage(derefValue, library, false, NO_ALLOC, indirect); + const int use = CheckUninitVarImpl::isFunctionParUsage(derefValue, library, false, CheckUninitVar::NO_ALLOC, indirect); return (use>0) ? derefValue : nullptr; } if (valueExpr->astParent()->isUnaryOp("&")) { const Token *parent = valueExpr->astParent(); if (Token::Match(parent->astParent(), "[(,]") && (parent->astParent()->str() == "," || astIsRhs(parent))) { - const int use = isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); + const int use = CheckUninitVarImpl::isFunctionParUsage(valueExpr, library, pointer, alloc, indirect); return (use>0) ? valueExpr : nullptr; } return nullptr; @@ -1264,13 +1316,13 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& // * Passing address in RHS to pointer variable { const Token *tok = derefValue ? derefValue : valueExpr; - if (alloc == NO_ALLOC) { + if (alloc == CheckUninitVar::NO_ALLOC) { while (tok->valueType() && tok->valueType()->pointer == 0 && Token::simpleMatch(tok->astParent(), ".")) tok = tok->astParent(); } if (Token::simpleMatch(tok->astParent(), "=")) { if (astIsLhs(tok)) { - if (alloc == ARRAY || !derefValue || !derefValue->isUnaryOp("*") || !pointer) + if (alloc == CheckUninitVar::ARRAY || !derefValue || !derefValue->isUnaryOp("*") || !pointer) return nullptr; const Token* deref = derefValue->astOperand1(); while (deref && deref->isCast()) @@ -1278,7 +1330,7 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& if (deref == vartok || Token::simpleMatch(deref, "+")) return nullptr; } - if (alloc != NO_ALLOC && astIsRhs(valueExpr)) + if (alloc != CheckUninitVar::NO_ALLOC && astIsRhs(valueExpr)) return nullptr; } } @@ -1314,7 +1366,7 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& if (vt->type == ValueType::Type::VOID) return nullptr; // passing a char* to a stream will dereference it - if ((alloc == CTOR_CALL || alloc == ARRAY) && vt->pointer && vt->type != ValueType::Type::CHAR && vt->type != ValueType::Type::WCHAR_T) + if ((alloc == CheckUninitVar::CTOR_CALL || alloc == CheckUninitVar::ARRAY) && vt->pointer && vt->type != ValueType::Type::CHAR && vt->type != ValueType::Type::WCHAR_T) return nullptr; } } @@ -1338,18 +1390,13 @@ const Token* CheckUninitVar::isVariableUsage(const Token *vartok, const Library& return derefValue ? derefValue : valueExpr; } -const Token* CheckUninitVar::isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const -{ - return isVariableUsage(vartok, mSettings->library, pointer, alloc, indirect); -} - /*** * Is function parameter "used" so a "usage of uninitialized variable" can * be written? If parameter is passed "by value" then it is "used". If it * is passed "by reference" then it is not necessarily "used". * @return -1 => unknown 0 => not used 1 => used */ -int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, Alloc alloc, int indirect) +int CheckUninitVarImpl::isFunctionParUsage(const Token *vartok, const Library& library, bool pointer, CheckUninitVar::Alloc alloc, int indirect) { bool unknown = false; const Token *parent = getAstParentSkipPossibleCastAndAddressOf(vartok, &unknown); @@ -1370,7 +1417,7 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& libra return -1; if (Token::simpleMatch(start->link(), ") {") && Token::Match(start->previous(), "if|for|while|switch")) - return (!pointer || alloc == NO_ALLOC); + return (!pointer || alloc == CheckUninitVar::NO_ALLOC); // is this a function call? if (Token::Match(start->previous(), "%name% (")) { @@ -1384,7 +1431,7 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& libra const Token *argStart = arg->typeStartToken(); if (!address && !array && Token::Match(argStart, "%type% %name%| [,)]")) return 1; - if (pointer && !address && alloc == NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) + if (pointer && !address && alloc == CheckUninitVar::NO_ALLOC && Token::Match(argStart, "%type% * %name% [,)]")) return 1; while (argStart->previous() && argStart->previous()->isName()) argStart = argStart->previous(); @@ -1400,14 +1447,14 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& libra } else if (Token::Match(start->previous(), "if|while|for")) { // control-flow statement reading the variable "by value" - return alloc == NO_ALLOC; + return alloc == CheckUninitVar::NO_ALLOC; } else { const bool isnullbad = library.isnullargbad(start->previous(), argumentNumber + 1); - if (indirect == 0 && pointer && !address && isnullbad && alloc == NO_ALLOC) + if (indirect == 0 && pointer && !address && isnullbad && alloc == CheckUninitVar::NO_ALLOC) return 1; bool hasIndirect = false; const bool isuninitbad = library.isuninitargbad(start->previous(), argumentNumber + 1, indirect, &hasIndirect); - if (alloc != NO_ALLOC) + if (alloc != CheckUninitVar::NO_ALLOC) return (isnullbad || hasIndirect) && isuninitbad; return isuninitbad && (!address || isnullbad); } @@ -1417,12 +1464,12 @@ int CheckUninitVar::isFunctionParUsage(const Token *vartok, const Library& libra return -1; } -int CheckUninitVar::isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect) const +int CheckUninitVarImpl::isFunctionParUsage(const Token *vartok, bool pointer, CheckUninitVar::Alloc alloc, int indirect) const { - return CheckUninitVar::isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect); + return isFunctionParUsage(vartok, mSettings->library, pointer, alloc, indirect); } -bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const +bool CheckUninitVarImpl::isMemberVariableAssignment(const Token *tok, const std::string &membervar) const { if (Token::Match(tok, "%name% . %name%") && tok->strAt(2) == membervar) { if (Token::Match(tok->tokAt(3), "[=.[]")) @@ -1436,7 +1483,7 @@ bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::str else if (tok->tokAt(3)->isConstOp()) ; // member variable usage else if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && - 1 == isFunctionParUsage(tok, false, NO_ALLOC)) { + 1 == isFunctionParUsage(tok, false, CheckUninitVar::NO_ALLOC)) { return false; } else return true; @@ -1491,7 +1538,7 @@ bool CheckUninitVar::isMemberVariableAssignment(const Token *tok, const std::str return false; } -bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const +bool CheckUninitVarImpl::isMemberVariableUsage(const Token *tok, bool isPointer, CheckUninitVar::Alloc alloc, const std::string &membervar) const { if (Token::Match(tok->previous(), "[(,] %name% . %name% [,)]") && tok->strAt(2) == membervar) { @@ -1507,7 +1554,7 @@ bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, All const Token *parent = tok->next()->astParent(); return !parent || !parent->isUnaryOp("&"); } - if (!isPointer && !Token::simpleMatch(tok->astParent(), ".") && Token::Match(tok->previous(), "[(,] %name% [,)]") && isVariableUsage(tok, isPointer, alloc)) + if (!isPointer && !Token::simpleMatch(tok->astParent(), ".") && Token::Match(tok->previous(), "[(,] %name% [,)]") && CheckUninitVar::isVariableUsage(tok, mSettings->library, isPointer, alloc)) return true; if (!isPointer && Token::Match(tok->previous(), "= %name% ;")) { @@ -1527,18 +1574,18 @@ bool CheckUninitVar::isMemberVariableUsage(const Token *tok, bool isPointer, All if ((false) && // NOLINT(readability-simplify-boolean-expr) !isPointer && Token::Match(tok->tokAt(-2), "[(,] & %name% [,)]") && - isVariableUsage(tok, isPointer, alloc)) + CheckUninitVar::isVariableUsage(tok, mSettings->library,isPointer, alloc)) return true; return false; } -void CheckUninitVar::uninitdataError(const Token *tok, const std::string &varname) +void CheckUninitVarImpl::uninitdataError(const Token *tok, const std::string &varname) { reportError(tok, Severity::error, "uninitdata", "$symbol:" + varname + "\nMemory is allocated but not initialized: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } -void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath) +void CheckUninitVarImpl::uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath) { if (diag(tok)) return; @@ -1551,7 +1598,7 @@ void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname Certainty::normal); } -void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) +void CheckUninitVarImpl::uninitvarError(const Token* tok, const ValueFlow::Value& v) { if (!mSettings->isEnabled(&v)) return; @@ -1588,7 +1635,7 @@ void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v) certainty); } -void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername) +void CheckUninitVarImpl::uninitStructMemberError(const Token *tok, const std::string &membername) { reportError(tok, Severity::error, @@ -1596,7 +1643,7 @@ void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string "$symbol:" + membername + "\nUninitialized struct member: $symbol", CWE_USE_OF_UNINITIALIZED_VARIABLE, Certainty::normal); } -void CheckUninitVar::valueFlowUninit() +void CheckUninitVarImpl::valueFlowUninit() { logChecker("CheckUninitVar::valueFlowUninit"); @@ -1759,3 +1806,20 @@ bool CheckUninitVar::analyseWholeProgram(const CTU::FileInfo *ctu, const std::li } return foundErrors; } + +void CheckUninitVar::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckUninitVarImpl checkUninitVar(&tokenizer, tokenizer.getSettings(), errorLogger); + checkUninitVar.valueFlowUninit(); + checkUninitVar.check(); +} + +void CheckUninitVar::getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const +{ + CheckUninitVarImpl c(nullptr, settings, errorLogger); + + ValueFlow::Value v{}; + + c.uninitvarError(nullptr, v); + c.uninitdataError(nullptr, "varname"); + c.uninitStructMemberError(nullptr, "a.b"); +} diff --git a/lib/checkuninitvar.h b/lib/checkuninitvar.h index 9b6352aec6f..340b68a2772 100644 --- a/lib/checkuninitvar.h +++ b/lib/checkuninitvar.h @@ -24,22 +24,17 @@ #include "check.h" #include "config.h" -#include "mathlib.h" #include "errortypes.h" -#include "tokenize.h" -#include "vfvalue.h" #include -#include -#include #include class Scope; class Token; -class Variable; class ErrorLogger; class Settings; class Library; +class Tokenizer; namespace CTU { class FileInfo; @@ -50,12 +45,6 @@ namespace tinyxml2 { } -struct VariableValue { - explicit VariableValue(MathLib::bigint val = 0) : value(val) {} - MathLib::bigint value; - bool notEqual{}; -}; - /// @addtogroup Checks /// @{ @@ -67,43 +56,15 @@ class CPPCHECKLIB CheckUninitVar : public Check { public: /** @brief This constructor is used when registering the CheckUninitVar */ - CheckUninitVar() : Check(myName()) {} + CheckUninitVar() : Check("Uninitialized variables") {} enum Alloc { NO_ALLOC, NO_CTOR_CALL, CTOR_CALL, ARRAY }; static const Token *isVariableUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); - const Token *isVariableUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; private: - /** @brief This constructor is used when running checks. */ - CheckUninitVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckUninitVar checkUninitVar(&tokenizer, tokenizer.getSettings(), errorLogger); - checkUninitVar.valueFlowUninit(); - checkUninitVar.check(); - } - - bool diag(const Token* tok); - /** Check for uninitialized variables */ - void check(); - void checkScope(const Scope* scope, const std::set &arrayTypeDefs); - void checkStruct(const Token *tok, const Variable &structvar); - bool checkScopeForVariable(const Token *tok, const Variable& var, bool* const possibleInit, bool* const noreturn, Alloc* const alloc, const std::string &membervar, std::map& variableValue); - const Token* checkExpr(const Token* tok, const Variable& var, const Alloc alloc, bool known, bool* bailout = nullptr) const; - bool checkIfForWhileHead(const Token *startparentheses, const Variable& var, bool suppressErrors, bool isuninit, Alloc alloc, const std::string &membervar); - bool checkLoopBody(const Token *tok, const Variable& var, const Alloc alloc, const std::string &membervar, const bool suppressErrors); - const Token* checkLoopBodyRecursive(const Token *start, const Variable& var, const Alloc alloc, const std::string &membervar, bool &bailout) const; - void checkRhs(const Token *tok, const Variable &var, Alloc alloc, nonneg int number_of_if, const std::string &membervar); - static int isFunctionParUsage(const Token *vartok, const Library &library, bool pointer, Alloc alloc, int indirect = 0); - int isFunctionParUsage(const Token *vartok, bool pointer, Alloc alloc, int indirect = 0) const; - bool isMemberVariableAssignment(const Token *tok, const std::string &membervar) const; - bool isMemberVariableUsage(const Token *tok, bool isPointer, Alloc alloc, const std::string &membervar) const; - - /** ValueFlow-based checking for uninitialized variables */ - void valueFlowUninit(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** @brief Parse current TU and extract file info */ Check::FileInfo *getFileInfo(const Tokenizer *tokenizer, const Settings *settings) const override; @@ -113,37 +74,7 @@ class CPPCHECKLIB CheckUninitVar : public Check { /** @brief Analyse all file infos for all TU */ bool analyseWholeProgram(const CTU::FileInfo *ctu, const std::list &fileInfo, const Settings& settings, ErrorLogger &errorLogger) override; - void uninitvarError(const Token* tok, const ValueFlow::Value& v); - void uninitdataError(const Token *tok, const std::string &varname); - void uninitvarError(const Token *tok, const std::string &varname, ErrorPath errorPath); - void uninitvarError(const Token *tok, const std::string &varname) { - ErrorPath errorPath; - uninitvarError(tok, varname, errorPath); - } - void uninitvarError(const Token *tok, const std::string &varname, Alloc alloc) { - if (alloc == NO_CTOR_CALL || alloc == CTOR_CALL) - uninitdataError(tok, varname); - else - uninitvarError(tok, varname); - } - void uninitStructMemberError(const Token *tok, const std::string &membername); - - std::set mUninitDiags; - - void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override - { - CheckUninitVar c(nullptr, settings, errorLogger); - - ValueFlow::Value v{}; - - c.uninitvarError(nullptr, v); - c.uninitdataError(nullptr, "varname"); - c.uninitStructMemberError(nullptr, "a.b"); - } - - static std::string myName() { - return "Uninitialized variables"; - } + void getErrorMessages(ErrorLogger* errorLogger, const Settings* settings) const override; std::string classInfo() const override { return "Uninitialized variables\n" diff --git a/lib/checkunusedfunctions.cpp b/lib/checkunusedfunctions.cpp index 24151998ce1..61e94c0865f 100644 --- a/lib/checkunusedfunctions.cpp +++ b/lib/checkunusedfunctions.cpp @@ -21,6 +21,7 @@ #include "checkunusedfunctions.h" #include "astutils.h" +#include "checkimpl.h" #include "errorlogger.h" #include "errortypes.h" #include "library.h" @@ -56,6 +57,57 @@ namespace { static const CWE CWE561(561U); // Dead Code +namespace { + class CheckUnusedFunctionsImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckUnusedFunctionsImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + static void clear(); + + // Parse current tokens and determine.. + // * Check what functions are used + // * What functions are declared + void parseTokens(const Tokenizer &tokenizer, const char FileName[], const Settings *settings); + + // Return true if an error is reported. + bool check(ErrorLogger * const errorLogger, const Settings& settings) const; + + std::string analyzerInfo() const; + + /** @brief Combine and analyze all analyzerInfos for all TUs */ + static void analyseWholeProgram(const Settings &settings, ErrorLogger * const errorLogger, const std::string &buildDir); + + private: + /** + * Dummy implementation, just to provide error for --errorlist + */ + static void unusedFunctionError(ErrorLogger * const errorLogger, + const std::string &filename, unsigned int fileIndex, unsigned int lineNumber, + const std::string &funcname); + + struct FunctionUsage { + std::string filename; + unsigned int lineNumber{}; + unsigned int fileIndex{}; + bool usedSameFile{}; + bool usedOtherFile{}; + }; + + std::unordered_map mFunctions; + + class FunctionDecl { + public: + explicit FunctionDecl(const Function *f); + std::string functionName; + unsigned int lineNumber; + }; + std::list mFunctionDecl; + std::set mFunctionCalls; + }; +} + static std::string stripTemplateParameters(const std::string& funcName) { std::string name = funcName; const auto pos = name.find('<'); @@ -383,9 +435,8 @@ bool CheckUnusedFunctions::analyseWholeProgram(const CTU::FileInfo *ctu, const s { (void)ctu; (void)fileInfo; - CheckUnusedFunctions dummy(nullptr, &settings, &errorLogger); - dummy. - logChecker("CheckUnusedFunctions::analyseWholeProgram"); // unusedFunctions + CheckUnusedFunctionsImpl dummy(nullptr, &settings, &errorLogger); + //dummy.logChecker("CheckUnusedFunctions::analyseWholeProgram"); // TODO return check(&errorLogger, settings); } @@ -478,3 +529,7 @@ void CheckUnusedFunctions::analyseWholeProgram(const Settings &settings, ErrorLo } } } + +void CheckUnusedFunctions::getErrorMessages(ErrorLogger *errorLogger, const Settings * /*settings*/) const { + unusedFunctionError(errorLogger, emptyString, 0, 0, "funcName"); +} diff --git a/lib/checkunusedfunctions.h b/lib/checkunusedfunctions.h index 7568d87d60a..aa1d63c5027 100644 --- a/lib/checkunusedfunctions.h +++ b/lib/checkunusedfunctions.h @@ -46,11 +46,7 @@ namespace CTU { class CPPCHECKLIB CheckUnusedFunctions : public Check { public: /** @brief This constructor is used when registering the CheckUnusedFunctions */ - CheckUnusedFunctions() : Check(myName()) {} - - /** @brief This constructor is used when running checks. */ - CheckUnusedFunctions(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} + CheckUnusedFunctions() : Check("Unused functions") {} static void clear(); @@ -74,9 +70,7 @@ class CPPCHECKLIB CheckUnusedFunctions : public Check { static void analyseWholeProgram(const Settings &settings, ErrorLogger * const errorLogger, const std::string &buildDir); private: - void getErrorMessages(ErrorLogger *errorLogger, const Settings * /*settings*/) const override { - CheckUnusedFunctions::unusedFunctionError(errorLogger, emptyString, 0, 0, "funcName"); - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings * /*settings*/) const override; void runChecks(const Tokenizer & /*tokenizer*/, ErrorLogger * /*errorLogger*/) override {} @@ -87,10 +81,6 @@ class CPPCHECKLIB CheckUnusedFunctions : public Check { const std::string &filename, unsigned int fileIndex, unsigned int lineNumber, const std::string &funcname); - static std::string myName() { - return "Unused functions"; - } - std::string classInfo() const override { return "Check for functions that are never called\n"; } diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 161a309c61f..1a69cb97d9e 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -21,6 +21,7 @@ #include "checkunusedvar.h" #include "astutils.h" +#include "checkimpl.h" #include "errortypes.h" #include "fwdanalysis.h" #include "library.h" @@ -98,82 +99,84 @@ static bool isRaiiClass(const ValueType *valueType, bool cpp, bool defaultReturn return defaultReturn; } -/** - * @brief This class is used create a list of variables within a function. - */ -class Variables { -public: - enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none }; - - /** Store information about variable usage */ - class VariableUsage { +namespace { + /** + * @brief This class is used create a list of variables within a function. + */ + class Variables { public: - explicit VariableUsage(const Variable *var = nullptr, - VariableType type = standard, - bool read = false, - bool write = false, - bool modified = false, - bool allocateMemory = false) : - _var(var), - _lastAccess(var ? var->nameToken() : nullptr), - mType(type), - _read(read), - _write(write), - _modified(modified), - _allocateMemory(allocateMemory) {} - - /** variable is used.. set both read+write */ - void use() { - _read = true; - _write = true; - } + enum VariableType { standard, array, pointer, reference, pointerArray, referenceArray, pointerPointer, none }; + + /** Store information about variable usage */ + class VariableUsage { + public: + explicit VariableUsage(const Variable *var = nullptr, + VariableType type = standard, + bool read = false, + bool write = false, + bool modified = false, + bool allocateMemory = false) : + _var(var), + _lastAccess(var ? var->nameToken() : nullptr), + mType(type), + _read(read), + _write(write), + _modified(modified), + _allocateMemory(allocateMemory) {} + + /** variable is used.. set both read+write */ + void use() { + _read = true; + _write = true; + } - /** is variable unused? */ - bool unused() const { - return (!_read && !_write); - } + /** is variable unused? */ + bool unused() const { + return (!_read && !_write); + } - std::set _aliases; - std::set _assignments; + std::set _aliases; + std::set _assignments; - const Variable* _var; - const Token* _lastAccess; - VariableType mType; - bool _read; - bool _write; - bool _modified; // read/modify/write - bool _allocateMemory; - }; + const Variable* _var; + const Token* _lastAccess; + VariableType mType; + bool _read; + bool _write; + bool _modified; // read/modify/write + bool _allocateMemory; + }; - void clear() { - mVarUsage.clear(); - } - const std::map &varUsage() const { - return mVarUsage; - } - void addVar(const Variable *var, VariableType type, bool write_); - void allocateMemory(nonneg int varid, const Token* tok); - void read(nonneg int varid, const Token* tok); - void readAliases(nonneg int varid, const Token* tok); - void readAll(nonneg int varid, const Token* tok); - void write(nonneg int varid, const Token* tok); - void writeAliases(nonneg int varid, const Token* tok); - void writeAll(nonneg int varid, const Token* tok); - void use(nonneg int varid, const Token* tok); - void modified(nonneg int varid, const Token* tok); - VariableUsage *find(nonneg int varid); - void alias(nonneg int varid1, nonneg int varid2, bool replace); - void erase(nonneg int varid) { - mVarUsage.erase(varid); - } - void eraseAliases(nonneg int varid); - void eraseAll(nonneg int varid); - void clearAliases(nonneg int varid); + void clear() { + mVarUsage.clear(); + } + const std::map &varUsage() const { + return mVarUsage; + } + void addVar(const Variable *var, VariableType type, bool write_); + void allocateMemory(nonneg int varid, const Token* tok); + void read(nonneg int varid, const Token* tok); + void readAliases(nonneg int varid, const Token* tok); + void readAll(nonneg int varid, const Token* tok); + void write(nonneg int varid, const Token* tok); + void writeAliases(nonneg int varid, const Token* tok); + void writeAll(nonneg int varid, const Token* tok); + void use(nonneg int varid, const Token* tok); + void modified(nonneg int varid, const Token* tok); + VariableUsage *find(nonneg int varid); + void alias(nonneg int varid1, nonneg int varid2, bool replace); + void erase(nonneg int varid) { + mVarUsage.erase(varid); + } + void eraseAliases(nonneg int varid); + void eraseAll(nonneg int varid); + void clearAliases(nonneg int varid); -private: + private: - std::map mVarUsage; -}; + std::map mVarUsage; + }; +} /** @@ -627,6 +630,38 @@ static const Token* doAssignment(Variables &variables, const Token *tok, bool de return tok; } +namespace { + class CheckUnusedVarImpl : public CheckImpl { + public: + /** @brief This constructor is used when running checks. */ + CheckUnusedVarImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + /** @brief %Check for unused function variables */ + void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables); + void checkFunctionVariableUsage(); + + /** @brief %Check that all struct members are used */ + void checkStructMemberUsage(); + + bool isRecordTypeWithoutSideEffects(const Type* type); + bool isVariableWithoutSideEffects(const Variable& var); + bool isEmptyType(const Type* type); + bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, + std::list checkedFuncs); + + // Error messages.. + void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, const std::string& prefix = "struct"); + void unusedVariableError(const Token *tok, const std::string &varname); + void allocatedButUnusedVariableError(const Token *tok, const std::string &varname); + void unreadVariableError(const Token *tok, const std::string &varname, bool modified); + void unassignedVariableError(const Token *tok, const std::string &varname); + + std::map mIsRecordTypeWithoutSideEffectsMap; + std::map mIsEmptyTypeMap; + }; +} + static bool isPartOfClassStructUnion(const Token* tok) { for (; tok; tok = tok->previous()) { @@ -688,7 +723,7 @@ static void useFunctionArgs(const Token *tok, Variables& variables) //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- -void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) +void CheckUnusedVarImpl::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) { // Find declarations if the scope is executable.. if (scope->isExecutable()) { @@ -1147,7 +1182,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const } } -void CheckUnusedVar::checkFunctionVariableUsage() +void CheckUnusedVarImpl::checkFunctionVariableUsage() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->checkLibrary) return; @@ -1398,7 +1433,7 @@ void CheckUnusedVar::checkFunctionVariableUsage() } } -void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &varname) +void CheckUnusedVarImpl::unusedVariableError(const Token *tok, const std::string &varname) { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1406,7 +1441,7 @@ void CheckUnusedVar::unusedVariableError(const Token *tok, const std::string &va reportError(tok, Severity::style, "unusedVariable", "$symbol:" + varname + "\nUnused variable: $symbol", CWE563, Certainty::normal); } -void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) +void CheckUnusedVarImpl::allocatedButUnusedVariableError(const Token *tok, const std::string &varname) { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1414,7 +1449,7 @@ void CheckUnusedVar::allocatedButUnusedVariableError(const Token *tok, const std reportError(tok, Severity::style, "unusedAllocatedMemory", "$symbol:" + varname + "\nVariable '$symbol' is allocated memory that is never used.", CWE563, Certainty::normal); } -void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &varname, bool modified) +void CheckUnusedVarImpl::unreadVariableError(const Token *tok, const std::string &varname, bool modified) { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1425,7 +1460,7 @@ void CheckUnusedVar::unreadVariableError(const Token *tok, const std::string &va reportError(tok, Severity::style, "unreadVariable", "$symbol:" + varname + "\nVariable '$symbol' is assigned a value that is never used.", CWE563, Certainty::normal); } -void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string &varname) +void CheckUnusedVarImpl::unassignedVariableError(const Token *tok, const std::string &varname) { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1436,7 +1471,7 @@ void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string //--------------------------------------------------------------------------- // Check that all struct members are used //--------------------------------------------------------------------------- -void CheckUnusedVar::checkStructMemberUsage() +void CheckUnusedVarImpl::checkStructMemberUsage() { if (!mSettings->severity.isEnabled(Severity::style)) return; @@ -1547,12 +1582,12 @@ void CheckUnusedVar::checkStructMemberUsage() } } -void CheckUnusedVar::unusedStructMemberError(const Token* tok, const std::string& structname, const std::string& varname, const std::string& prefix) +void CheckUnusedVarImpl::unusedStructMemberError(const Token* tok, const std::string& structname, const std::string& varname, const std::string& prefix) { reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + " member '$symbol' is never used.", CWE563, Certainty::normal); } -bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) +bool CheckUnusedVarImpl::isRecordTypeWithoutSideEffects(const Type* type) { // a type that has no side effects (no constructors and no members with constructors) /** @todo false negative: check constructors for side effects */ @@ -1628,7 +1663,7 @@ bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) return (withoutSideEffects = true); } -bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var) +bool CheckUnusedVarImpl::isVariableWithoutSideEffects(const Variable& var) { if (var.isPointer()) return true; @@ -1648,7 +1683,7 @@ bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var) return true; } -bool CheckUnusedVar::isEmptyType(const Type* type) +bool CheckUnusedVarImpl::isEmptyType(const Type* type) { // a type that has no variables and no constructor @@ -1669,7 +1704,7 @@ bool CheckUnusedVar::isEmptyType(const Type* type) return (emptyType = false); } -bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, +bool CheckUnusedVarImpl::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, std::list checkedFuncs) { // no body to analyze @@ -1747,3 +1782,21 @@ bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const To return !sideEffectReturnFound; } + +void CheckUnusedVar::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { + CheckUnusedVarImpl checkUnusedVar(&tokenizer, tokenizer.getSettings(), errorLogger); + + // Coding style checks + checkUnusedVar.checkStructMemberUsage(); + checkUnusedVar.checkFunctionVariableUsage(); +} + +void CheckUnusedVar::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const { + CheckUnusedVarImpl c(nullptr, settings, errorLogger); + c.unusedVariableError(nullptr, "varname"); + c.allocatedButUnusedVariableError(nullptr, "varname"); + c.unreadVariableError(nullptr, "varname", false); + c.unassignedVariableError(nullptr, "varname"); + c.unusedStructMemberError(nullptr, "structname", "variable"); +} + diff --git a/lib/checkunusedvar.h b/lib/checkunusedvar.h index 0275c55303b..948b50c2158 100644 --- a/lib/checkunusedvar.h +++ b/lib/checkunusedvar.h @@ -23,20 +23,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" -#include -#include #include class ErrorLogger; -class Scope; class Settings; -class Token; -class Type; -class Variables; -class Variable; -class Function; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -49,54 +41,13 @@ class CPPCHECKLIB CheckUnusedVar : public Check { public: /** @brief This constructor is used when registering the CheckClass */ - CheckUnusedVar() : Check(myName()) {} + CheckUnusedVar() : Check("UnusedVar") {} private: - /** @brief This constructor is used when running checks. */ - CheckUnusedVar(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - /** @brief Run checks against the normal token list */ - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckUnusedVar checkUnusedVar(&tokenizer, tokenizer.getSettings(), errorLogger); - - // Coding style checks - checkUnusedVar.checkStructMemberUsage(); - checkUnusedVar.checkFunctionVariableUsage(); - } - - /** @brief %Check for unused function variables */ - void checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables); - void checkFunctionVariableUsage(); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - /** @brief %Check that all struct members are used */ - void checkStructMemberUsage(); - - bool isRecordTypeWithoutSideEffects(const Type* type); - bool isVariableWithoutSideEffects(const Variable& var); - bool isEmptyType(const Type* type); - bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, - std::list checkedFuncs); - - // Error messages.. - void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, const std::string& prefix = "struct"); - void unusedVariableError(const Token *tok, const std::string &varname); - void allocatedButUnusedVariableError(const Token *tok, const std::string &varname); - void unreadVariableError(const Token *tok, const std::string &varname, bool modified); - void unassignedVariableError(const Token *tok, const std::string &varname); - - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckUnusedVar c(nullptr, settings, errorLogger); - c.unusedVariableError(nullptr, "varname"); - c.allocatedButUnusedVariableError(nullptr, "varname"); - c.unreadVariableError(nullptr, "varname", false); - c.unassignedVariableError(nullptr, "varname"); - c.unusedStructMemberError(nullptr, "structname", "variable"); - } - - static std::string myName() { - return "UnusedVar"; - } + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "UnusedVar checks\n" @@ -108,11 +59,6 @@ class CPPCHECKLIB CheckUnusedVar : public Check { "- unassigned variable\n" "- unused struct member\n"; } - - std::map mIsRecordTypeWithoutSideEffectsMap; - - std::map mIsEmptyTypeMap; - }; /// @} //--------------------------------------------------------------------------- diff --git a/lib/checkvaarg.cpp b/lib/checkvaarg.cpp index 3606a4f3432..a614f950ba4 100644 --- a/lib/checkvaarg.cpp +++ b/lib/checkvaarg.cpp @@ -19,6 +19,7 @@ #include "checkvaarg.h" #include "astutils.h" +#include "checkimpl.h" #include "errortypes.h" #include "settings.h" #include "symboldatabase.h" @@ -47,7 +48,25 @@ static const CWE CWE664(664U); // Improper Control of a Resource Through its L static const CWE CWE688(688U); // Function Call With Incorrect Variable or Reference as Argument static const CWE CWE758(758U); // Reliance on Undefined, Unspecified, or Implementation-Defined Behavior -void CheckVaarg::va_start_argument() +namespace { + class CheckVaargImpl: public CheckImpl + { + public: + CheckVaargImpl(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) + : CheckImpl(tokenizer, settings, errorLogger) {} + + void va_start_argument(); + void va_list_usage(); + + void wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName); + void referenceAs_va_start_error(const Token *tok, const std::string& paramName); + void va_end_missingError(const Token *tok, const std::string& varname); + void va_list_usedBeforeStartedError(const Token *tok, const std::string& varname); + void va_start_subsequentCallsError(const Token *tok, const std::string& varname); + }; +} + +void CheckVaargImpl::va_start_argument() { const SymbolDatabase* const symbolDatabase = mTokenizer->getSymbolDatabase(); const std::size_t functions = symbolDatabase->functionScopes.size(); @@ -81,13 +100,13 @@ void CheckVaarg::va_start_argument() } } -void CheckVaarg::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName) +void CheckVaargImpl::wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName) { reportError(tok, Severity::warning, "va_start_wrongParameter", "'" + paramIsName + "' given to va_start() is not last named argument of the function. Did you intend to pass '" + paramShouldName + "'?", CWE688, Certainty::normal); } -void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& paramName) +void CheckVaargImpl::referenceAs_va_start_error(const Token *tok, const std::string& paramName) { reportError(tok, Severity::error, "va_start_referencePassed", "Using reference '" + paramName + "' as parameter for va_start() results in undefined behaviour.", CWE758, Certainty::normal); @@ -98,7 +117,7 @@ void CheckVaarg::referenceAs_va_start_error(const Token *tok, const std::string& // Detect va_list usage after va_end() //--------------------------------------------------------------------------- -void CheckVaarg::va_list_usage() +void CheckVaargImpl::va_list_usage() { if (mSettings->clang) return; @@ -163,20 +182,37 @@ void CheckVaarg::va_list_usage() } } -void CheckVaarg::va_end_missingError(const Token *tok, const std::string& varname) +void CheckVaargImpl::va_end_missingError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_end_missing", "va_list '" + varname + "' was opened but not closed by va_end().", CWE664, Certainty::normal); } -void CheckVaarg::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname) +void CheckVaargImpl::va_list_usedBeforeStartedError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_list_usedBeforeStarted", "va_list '" + varname + "' used before va_start() was called.", CWE664, Certainty::normal); } -void CheckVaarg::va_start_subsequentCallsError(const Token *tok, const std::string& varname) +void CheckVaargImpl::va_start_subsequentCallsError(const Token *tok, const std::string& varname) { reportError(tok, Severity::error, "va_start_subsequentCalls", "va_start() or va_copy() called subsequently on '" + varname + "' without va_end() in between.", CWE664, Certainty::normal); } + +void CheckVaarg::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) +{ + CheckVaargImpl check(&tokenizer, tokenizer.getSettings(), errorLogger); + check.va_start_argument(); + check.va_list_usage(); +} + +void CheckVaarg::getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const +{ + CheckVaargImpl c(nullptr, settings, errorLogger); + c.wrongParameterTo_va_start_error(nullptr, "arg1", "arg2"); + c.referenceAs_va_start_error(nullptr, "arg1"); + c.va_end_missingError(nullptr, "vl"); + c.va_list_usedBeforeStartedError(nullptr, "vl"); + c.va_start_subsequentCallsError(nullptr, "vl"); +} diff --git a/lib/checkvaarg.h b/lib/checkvaarg.h index bfeec28f9a7..79657fbbc51 100644 --- a/lib/checkvaarg.h +++ b/lib/checkvaarg.h @@ -24,13 +24,12 @@ #include "check.h" #include "config.h" -#include "tokenize.h" #include class ErrorLogger; class Settings; -class Token; +class Tokenizer; /// @addtogroup Checks /// @{ @@ -41,39 +40,12 @@ class Token; class CPPCHECKLIB CheckVaarg : public Check { public: - CheckVaarg() : Check(myName()) {} + CheckVaarg() : Check("Vaarg") {} -private: - CheckVaarg(const Tokenizer *tokenizer, const Settings *settings, ErrorLogger *errorLogger) - : Check(myName(), tokenizer, settings, errorLogger) {} - - void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override { - CheckVaarg check(&tokenizer, tokenizer.getSettings(), errorLogger); - check.va_start_argument(); - check.va_list_usage(); - } - - void va_start_argument(); - void va_list_usage(); - - void wrongParameterTo_va_start_error(const Token *tok, const std::string& paramIsName, const std::string& paramShouldName); - void referenceAs_va_start_error(const Token *tok, const std::string& paramName); - void va_end_missingError(const Token *tok, const std::string& varname); - void va_list_usedBeforeStartedError(const Token *tok, const std::string& varname); - void va_start_subsequentCallsError(const Token *tok, const std::string& varname); + void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; - void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override { - CheckVaarg c(nullptr, settings, errorLogger); - c.wrongParameterTo_va_start_error(nullptr, "arg1", "arg2"); - c.referenceAs_va_start_error(nullptr, "arg1"); - c.va_end_missingError(nullptr, "vl"); - c.va_list_usedBeforeStartedError(nullptr, "vl"); - c.va_start_subsequentCallsError(nullptr, "vl"); - } - - static std::string myName() { - return "Vaarg"; - } +private: + void getErrorMessages(ErrorLogger *errorLogger, const Settings *settings) const override; std::string classInfo() const override { return "Check for misusage of variable argument lists:\n" diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index f70bca725a2..6f2ab88f9a1 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -619,7 +619,8 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string mPlistFile.close(); } - CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); + // TODO + //CheckUnusedFunctions checkUnusedFunctions(nullptr, nullptr, nullptr); try { Preprocessor preprocessor(mSettings, this); @@ -655,7 +656,8 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string if (mSettings.library.markupFile(filename)) { Tokenizer tokenizer(&mSettings, this, &preprocessor); tokenizer.createTokens(std::move(tokens1)); - checkUnusedFunctions.getFileInfo(&tokenizer, &mSettings); + // TODO + //checkUnusedFunctions.getFileInfo(&tokenizer, &mSettings); return EXIT_SUCCESS; } @@ -916,9 +918,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string // Check normal tokens checkNormalTokens(tokenizer); + // TODO // Analyze info.. - if (!mSettings.buildDir.empty()) - checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings); + //if (!mSettings.buildDir.empty()) + // checkUnusedFunctions.parseTokens(tokenizer, filename.c_str(), &mSettings); #ifdef HAVE_RULES // handling of "simple" rules has been removed. @@ -1000,7 +1003,8 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string } if (!mSettings.buildDir.empty()) { - mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); + // TODO + //mAnalyzerInformation.setFileInfo("CheckUnusedFunctions", checkUnusedFunctions.analyzerInfo()); mAnalyzerInformation.close(); } diff --git a/lib/cppcheck.vcxproj b/lib/cppcheck.vcxproj index e53ce4d856b..95241096157 100644 --- a/lib/cppcheck.vcxproj +++ b/lib/cppcheck.vcxproj @@ -44,6 +44,7 @@ + @@ -110,6 +111,7 @@ + diff --git a/lib/lib.pri b/lib/lib.pri index 5125fb684a0..e835bfe639a 100644 --- a/lib/lib.pri +++ b/lib/lib.pri @@ -21,6 +21,7 @@ HEADERS += $${PWD}/addoninfo.h \ $${PWD}/checkersreport.h \ $${PWD}/checkexceptionsafety.h \ $${PWD}/checkfunctions.h \ + $${PWD}/checkimpl.h \ $${PWD}/checkinternal.h \ $${PWD}/checkio.h \ $${PWD}/checkleakautovar.h \ @@ -99,6 +100,7 @@ SOURCES += $${PWD}/valueflow.cpp \ $${PWD}/checkersreport.cpp \ $${PWD}/checkexceptionsafety.cpp \ $${PWD}/checkfunctions.cpp \ + $${PWD}/checkimpl.cpp \ $${PWD}/checkinternal.cpp \ $${PWD}/checkio.cpp \ $${PWD}/checkleakautovar.cpp \ diff --git a/test/test64bit.cpp b/test/test64bit.cpp index 72d54f91d1e..99490f653a5 100644 --- a/test/test64bit.cpp +++ b/test/test64bit.cpp @@ -53,8 +53,7 @@ class Test64BitPortability : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check char variable usage.. - Check64BitPortability check64BitPortability(&tokenizer, &settings, this); - check64BitPortability.pointerassignment(); + runChecks(tokenizer, this); } void assignment() { diff --git a/test/testcharvar.cpp b/test/testcharvar.cpp index 1936720e561..9fff006a9bf 100644 --- a/test/testcharvar.cpp +++ b/test/testcharvar.cpp @@ -50,8 +50,7 @@ class TestCharVar : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check char variable usage.. - CheckOther checkOther(&tokenizer, &settings, this); - checkOther.checkCharVariable(); + (CheckOther::checkCharVariable)(&tokenizer, &settings, this); } void array_index_1() { diff --git a/test/testclass.cpp b/test/testclass.cpp index 04a3f2f700c..7358261dc89 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -256,8 +256,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings, this); - (checkClass.checkCopyCtorAndEqOperator)(); + (CheckClass::checkCopyCtorAndEqOperator)(&tokenizer, &settings, this); } void copyCtorAndEqOperator() { @@ -362,8 +361,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings0, this); - (checkClass.checkExplicitConstructors)(); + (CheckClass::checkExplicitConstructors)(&tokenizer, &settings0, this); } void explicitConstructors() { @@ -516,8 +514,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings1, this); - (checkClass.checkDuplInheritedMembers)(); + (CheckClass::checkDuplInheritedMembers)(&tokenizer, &settings1, this); } void duplInheritedMembers() { @@ -719,8 +716,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings0, this); - checkClass.copyconstructors(); + CheckClass::copyconstructors(&tokenizer, &settings0, this); } void copyConstructor1() { @@ -1168,8 +1164,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings0, this); - checkClass.operatorEqRetRefThis(); + (CheckClass::operatorEqRetRefThis)(&tokenizer, &settings0, this); } void operatorEqRetRefThis1() { @@ -1644,8 +1639,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings1, this); - checkClass.operatorEqToSelf(); + CheckClass::operatorEqToSelf(&tokenizer, &settings1, this); } void operatorEqToSelf1() { @@ -2609,8 +2603,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings0, this); - checkClass.virtualDestructor(); + CheckClass::virtualDestructor(&tokenizer, &settings0, this); } void virtualDestructor1() { @@ -2951,8 +2944,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings, this); - checkClass.checkMemset(); + CheckClass::checkMemset(&tokenizer, &settings, this); } void memsetOnClass() { @@ -3582,8 +3574,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings1, this); - checkClass.thisSubtraction(); + CheckClass::thisSubtraction(&tokenizer, &settings1, this); } void this_subtraction() { @@ -3618,8 +3609,7 @@ class TestClass : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckClass checkClass(&tokenizer, &settings, this); - (checkClass.checkConst)(); + (CheckClass::checkConst)(&tokenizer, &settings, this); } void const1() { @@ -7532,8 +7522,7 @@ class TestClass : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckClass checkClass(&tokenizer, &settings0, this); - checkClass.initializerListOrder(); + CheckClass::initializerListOrder(&tokenizer, &settings0, this); } void initializerListOrder() { @@ -7569,8 +7558,7 @@ class TestClass : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckClass checkClass(&tokenizer, &settings, this); - checkClass.initializationListUsage(); + CheckClass::initializationListUsage(&tokenizer, &settings, this); } void initializerListUsage() { @@ -7783,8 +7771,7 @@ class TestClass : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckClass checkClass(&tokenizer, &settings0, this); - (checkClass.checkSelfInitialization)(); + (CheckClass::checkSelfInitialization)(&tokenizer, &settings0, this); } void selfInitialization() { @@ -7899,8 +7886,7 @@ class TestClass : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckClass checkClass(&tokenizer, &settings, this); - checkClass.checkVirtualFunctionCallInConstructor(); + CheckClass::checkVirtualFunctionCallInConstructor(&tokenizer, &settings, this); } void virtualFunctionCallInConstructor() { @@ -8249,8 +8235,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings, this); - (checkClass.checkOverride)(); + (CheckClass::checkOverride)(&tokenizer, &settings, this); } void override1() { @@ -8436,8 +8421,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings, this); - (checkClass.checkUselessOverride)(); + (CheckClass::checkUselessOverride)(&tokenizer, &settings, this); } void uselessOverride() { @@ -8611,8 +8595,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings, this); - (checkClass.checkUnsafeClassRefMember)(); + (CheckClass::checkUnsafeClassRefMember)(&tokenizer, &settings, this); } void unsafeClassRefMember() { @@ -8634,8 +8617,7 @@ class TestClass : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check.. - CheckClass checkClass(&tokenizer, &settings1, this); - (checkClass.checkThisUseAfterFree)(); + (CheckClass::checkThisUseAfterFree)(&tokenizer, &settings1, this); } void thisUseAfterFree() { diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 77ab92d8677..d747824e2a9 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -47,8 +47,7 @@ class TestConstructors : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check class constructors.. - CheckClass checkClass(&tokenizer, &settings1, this); - checkClass.constructors(); + CheckClass::constructors(&tokenizer, &settings1, this); } void check_(const char* file, int line, const char code[], const Settings &s) { @@ -61,8 +60,7 @@ class TestConstructors : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check class constructors.. - CheckClass checkClass(&tokenizer, &s, this); - checkClass.constructors(); + CheckClass::constructors(&tokenizer, &s, this); } void run() override { diff --git a/test/testincompletestatement.cpp b/test/testincompletestatement.cpp index 1cf105b0ace..6d165da7365 100644 --- a/test/testincompletestatement.cpp +++ b/test/testincompletestatement.cpp @@ -49,8 +49,7 @@ class TestIncompleteStatement : public TestFixture { ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); // Check for incomplete statements.. - CheckOther checkOther(&tokenizer, &settings1, this); - checkOther.checkIncompleteStatement(); + CheckOther::checkIncompleteStatement(&tokenizer, &settings1, this); } void run() override { diff --git a/test/testio.cpp b/test/testio.cpp index 8f89f0a7bd5..cb07d33b125 100644 --- a/test/testio.cpp +++ b/test/testio.cpp @@ -103,8 +103,7 @@ class TestIO : public TestFixture { // Check.. if (onlyFormatStr) { - CheckIO checkIO(&tokenizer, &settings1, this); - checkIO.checkWrongPrintfScanfArguments(); + CheckIO::checkWrongPrintfScanfArguments(&tokenizer, &settings1, this); return; } runChecks(tokenizer, this); diff --git a/test/testother.cpp b/test/testother.cpp index a97f52b8dad..b5cf3e573ef 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1681,8 +1681,7 @@ class TestOther : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizerCpp.tokenize(istr, "test.cpp"), file, line); - CheckOther checkOtherCpp(&tokenizerCpp, &settings, this); - checkOtherCpp.warningOldStylePointerCast(); + CheckOther::warningOldStylePointerCast(&tokenizerCpp, &settings, this); } void oldStylePointerCast() { @@ -1883,8 +1882,7 @@ class TestOther : public TestFixture { std::istringstream istr(code); ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); - CheckOther checkOtherCpp(&tokenizer, &settings, this); - checkOtherCpp.invalidPointerCast(); + CheckOther::invalidPointerCast(&tokenizer, &settings, this); } diff --git a/test/testpostfixoperator.cpp b/test/testpostfixoperator.cpp index 48ff941f542..f4158d023b6 100644 --- a/test/testpostfixoperator.cpp +++ b/test/testpostfixoperator.cpp @@ -43,8 +43,7 @@ class TestPostfixOperator : public TestFixture { ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line); // Check for postfix operators.. - CheckPostfixOperator checkPostfixOperator(&tokenizer, &settings, this); - checkPostfixOperator.postfixOperator(); + runChecks(tokenizer, this); } void run() override { diff --git a/test/testunusedprivfunc.cpp b/test/testunusedprivfunc.cpp index a0842a8526a..065e34d0034 100644 --- a/test/testunusedprivfunc.cpp +++ b/test/testunusedprivfunc.cpp @@ -101,8 +101,7 @@ class TestUnusedPrivateFunction : public TestFixture { ASSERT_LOC(tokenizer.simplifyTokens1(""), file, line); // Check for unused private functions.. - CheckClass checkClass(&tokenizer, &settings1, this); - checkClass.privateFunctions(); + CheckClass::privateFunctions(&tokenizer, &settings1, this); } void test1() { From 94e7d7cd4df3683a6ffe7e9803d0baa05ac0801d Mon Sep 17 00:00:00 2001 From: firewave Date: Sun, 13 Aug 2023 21:56:34 +0200 Subject: [PATCH 2/2] remove --- .github/workflows/CI-cygwin.yml | 58 --- .github/workflows/CI-mingw.yml | 73 ---- .github/workflows/CI-unixish-docker.yml | 179 -------- .github/workflows/CI-unixish.yml | 520 ------------------------ .github/workflows/CI-windows.yml | 207 ---------- .github/workflows/asan.yml | 101 ----- .github/workflows/buildman.yml | 58 --- .github/workflows/cifuzz.yml | 26 -- .github/workflows/clang-tidy.yml | 73 ---- .github/workflows/codeql-analysis.yml | 54 --- .github/workflows/coverage.yml | 68 ---- .github/workflows/cppcheck-premium.yml | 43 -- .github/workflows/format.yml | 47 --- .github/workflows/iwyu.yml | 165 -------- .github/workflows/release-windows.yml | 169 -------- .github/workflows/scriptcheck.yml | 200 --------- .github/workflows/selfcheck.yml | 135 ------ .github/workflows/tsan.yml | 100 ----- .github/workflows/ubsan.yml | 97 ----- .github/workflows/valgrind.yml | 61 --- 20 files changed, 2434 deletions(-) delete mode 100644 .github/workflows/CI-cygwin.yml delete mode 100644 .github/workflows/CI-mingw.yml delete mode 100644 .github/workflows/CI-unixish-docker.yml delete mode 100644 .github/workflows/CI-unixish.yml delete mode 100644 .github/workflows/CI-windows.yml delete mode 100644 .github/workflows/asan.yml delete mode 100644 .github/workflows/buildman.yml delete mode 100644 .github/workflows/cifuzz.yml delete mode 100644 .github/workflows/clang-tidy.yml delete mode 100644 .github/workflows/codeql-analysis.yml delete mode 100644 .github/workflows/coverage.yml delete mode 100644 .github/workflows/cppcheck-premium.yml delete mode 100644 .github/workflows/format.yml delete mode 100644 .github/workflows/iwyu.yml delete mode 100644 .github/workflows/release-windows.yml delete mode 100644 .github/workflows/scriptcheck.yml delete mode 100644 .github/workflows/selfcheck.yml delete mode 100644 .github/workflows/tsan.yml delete mode 100644 .github/workflows/ubsan.yml delete mode 100644 .github/workflows/valgrind.yml diff --git a/.github/workflows/CI-cygwin.yml b/.github/workflows/CI-cygwin.yml deleted file mode 100644 index b9ea30470da..00000000000 --- a/.github/workflows/CI-cygwin.yml +++ /dev/null @@ -1,58 +0,0 @@ -# Some convenient links: -# - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md -# - -name: CI-cygwin - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -defaults: - run: - shell: cmd - -jobs: - build_cygwin: - strategy: - matrix: - os: [windows-2022] - arch: [x64] - include: - - platform: 'x86_64' - packages: | - gcc-g++ - python3 - fail-fast: false - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Set up Cygwin - uses: cygwin/cygwin-install-action@master - with: - platform: ${{ matrix.arch }} - packages: ${{ matrix.packages }} - - # Cygwin will always link the binaries even if they already exist. The linking is also extremely slow. So just run the "check" target which includes all the binaries. - - name: Build all and run test - run: | - C:\cygwin\bin\bash.exe -l -c cd %GITHUB_WORKSPACE% && make VERBOSE=1 -j2 check - - - name: Extra test for misra - run: | - cd %GITHUB_WORKSPACE%\addons\test - ..\..\cppcheck.exe --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 - python3 ..\misra.py -verify misra\misra-test.c.dump - ..\..\cppcheck.exe --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 misra\misra-ctu-1-test.c misra\misra-ctu-2-test.c - diff --git a/.github/workflows/CI-mingw.yml b/.github/workflows/CI-mingw.yml deleted file mode 100644 index 75de79ed1f7..00000000000 --- a/.github/workflows/CI-mingw.yml +++ /dev/null @@ -1,73 +0,0 @@ -# Some convenient links: -# - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md -# - -name: CI-mingw - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -defaults: - run: - shell: msys2 {0} - -jobs: - build_mingw: - strategy: - matrix: - # the MinGW installation in windows-2019 is supposed to be 8.1 but it is 12.2 - # the MinGW installation in windows-2022 is not including all necessary packages by default, so just use the older image instead - package versions are he same - os: [windows-2019] - fail-fast: false - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Set up MSYS2 - uses: msys2/setup-msys2@v2 - with: - release: false # use pre-installed - install: >- - mingw-w64-x86_64-lld - mingw-w64-x86_64-ccache - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - # TODO: bail out on warning - - name: Build cppcheck - run: | - export PATH="/mingw64/lib/ccache/bin:$PATH" - # set RDYNAMIC to work around broken MinGW detection - make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 cppcheck - env: - LDFLAGS: -fuse-ld=lld # use lld for faster linking - - - name: Build test - run: | - export PATH="/mingw64/lib/ccache/bin:$PATH" - # set RDYNAMIC to work around broken MinGW detection - make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 testrunner - env: - LDFLAGS: -fuse-ld=lld # use lld for faster linking - - - name: Run test - run: | - export PATH="/mingw64/lib/ccache/bin:$PATH" - # set RDYNAMIC to work around broken MinGW detection - make VERBOSE=1 RDYNAMIC=-lshlwapi -j2 check - env: - LDFLAGS: -fuse-ld=lld # use lld for faster linking diff --git a/.github/workflows/CI-unixish-docker.yml b/.github/workflows/CI-unixish-docker.yml deleted file mode 100644 index c5e51d5b4c0..00000000000 --- a/.github/workflows/CI-unixish-docker.yml +++ /dev/null @@ -1,179 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: CI-unixish-docker - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build_cmake: - - strategy: - matrix: - image: ["centos:7", "ubuntu:14.04", "ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] - include: - - build_gui: false - - image: "ubuntu:23.10" - build_gui: true - fail-fast: false # Prefer quick result - - runs-on: ubuntu-22.04 - - # TODO: is this actually applied to the guest? - env: - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - container: - image: ${{ matrix.image }} - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software on CentOS 7 - if: matrix.image == 'centos:7' - run: | - yum install -y cmake gcc-c++ make pcre-devel - yum --enablerepo=extras install -y epel-release - yum install -y ccache - - - name: Install missing software on ubuntu - if: contains(matrix.image, 'ubuntu') - run: | - apt-get update - apt-get install -y cmake g++ make libxml2-utils libpcre3-dev - - - name: Install missing software (gui) on latest ubuntu - if: matrix.build_gui - run: | - apt-get install -y qt6-base-dev qt6-charts-dev qt6-tools-dev - - # needs to be called after the package installation since - # - it doesn't call "apt-get update" - # - it doesn't support centos - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - if: matrix.image != 'ubuntu:14.04' # no support for --set-config - with: - key: ${{ github.workflow }}-${{ matrix.image }} - - # tests require CMake 3.9 - no ccache available - - name: CMake build (no tests / no ccache) - if: matrix.image == 'ubuntu:14.04' - run: | - mkdir cmake.output - cd cmake.output - cmake -G "Unix Makefiles" -DHAVE_RULES=On .. - cmake --build . -- -j$(nproc) - - # tests require CMake 3.9 - ccache available - - name: CMake build (no tests) - if: matrix.image == 'centos:7' || matrix.image == 'ubuntu:16.04' - run: | - mkdir cmake.output - cd cmake.output - cmake -G "Unix Makefiles" -DHAVE_RULES=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. - cmake --build . -- -j$(nproc) - - - name: CMake build - if: ${{ !matrix.build_gui && matrix.image != 'centos:7' && matrix.image != 'ubuntu:14.04' && matrix.image != 'ubuntu:16.04' }} - run: | - mkdir cmake.output - cd cmake.output - cmake -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache .. - cmake --build . -- -j$(nproc) - - - name: CMake build (with GUI) - if: matrix.build_gui - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build cmake.output -- -j$(nproc) - - - name: Run CMake test - if: matrix.image != 'centos:7' && matrix.image != 'ubuntu:14.04' && matrix.image != 'ubuntu:16.04' - run: | - cmake --build cmake.output --target check -- -j$(nproc) - - build_make: - - strategy: - matrix: - image: ["centos:7", "ubuntu:14.04", "ubuntu:16.04", "ubuntu:18.04", "ubuntu:23.10"] - fail-fast: false # Prefer quick result - - runs-on: ubuntu-22.04 - - container: - image: ${{ matrix.image }} - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software on CentOS 7 - if: matrix.image == 'centos:7' - run: | - yum install -y gcc-c++ make which python3 pcre-devel - yum --enablerepo=extras install -y epel-release - yum install -y ccache - - - name: Install missing software on ubuntu - if: contains(matrix.image, 'ubuntu') - run: | - apt-get update - apt-get install -y g++ make python3 libxml2-utils libpcre3-dev - - # needs to be called after the package installation since - # - it doesn't call "apt-get update" - # - it doesn't support centos - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - if: matrix.image != 'ubuntu:14.04' # no support for --set-config - with: - key: ${{ github.workflow }}-${{ matrix.image }} - - - name: Build cppcheck - run: | - # "/usr/lib64" for centos / "/usr/lib" for ubuntu - export PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) HAVE_RULES=yes CXXFLAGS="-w" - - - name: Build test - run: | - # "/usr/lib64" for centos / "/usr/lib" for ubuntu - export PATH="/usr/lib64/ccache:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) testrunner HAVE_RULES=yes CXXFLAGS="-w" - - - name: Run test - run: | - make -j$(nproc) check HAVE_RULES=yes - - # requires python3 - - name: Run extra tests - run: | - tools/generate_and_run_more_tests.sh - - # requires which - - name: Validate - run: | - make -j$(nproc) checkCWEEntries validateXML - - - name: Test addons - run: | - ./cppcheck --addon=threadsafety addons/test/threadsafety - ./cppcheck --addon=threadsafety --std=c++03 addons/test/threadsafety - - - name: Generate Qt help file on ubuntu 18.04 - if: false # matrix.os == 'ubuntu-18.04' - run: | - pushd gui/help - qcollectiongenerator online-help.qhcp -o online-help.qhc - diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml deleted file mode 100644 index 24d4de1faf5..00000000000 --- a/.github/workflows/CI-unixish.yml +++ /dev/null @@ -1,520 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: CI-unixish - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build_cmake_tinyxml2: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - include: - - use_qt6: On - - os: ubuntu-20.04 - use_qt6: Off - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - env: - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'Off' - run: | - sudo apt-get update - sudo apt-get install libxml2-utils libtinyxml2-dev qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'On' - run: | - sudo apt-get update - # qt6-tools-dev-tools for lprodump - # qt6-l10n-tools for lupdate - sudo apt-get install libxml2-utils libtinyxml2-dev qt6-base-dev libqt6charts6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools libglx-dev libgl1-mesa-dev - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - # pcre was removed from runner images in November 2022 - brew install coreutils qt@6 tinyxml2 pcre - - - name: CMake build on ubuntu (with GUI / system tinyxml2) - if: contains(matrix.os, 'ubuntu') - run: | - cmake -S . -B cmake.output.tinyxml2 -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=${{ matrix.use_qt6 }} -DWITH_QCHART=On -DUSE_BUNDLED_TINYXML2=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build cmake.output.tinyxml2 -- -j$(nproc) - - - name: CMake build on macos (with GUI / system tinyxml2) - if: contains(matrix.os, 'macos') - run: | - cmake -S . -B cmake.output.tinyxml2 -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DUSE_BUNDLED_TINYXML2=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DQt6_DIR=$(brew --prefix qt@6)/lib/cmake/Qt6 - cmake --build cmake.output.tinyxml2 -- -j$(nproc) - - - name: Run CMake test (system tinyxml2) - run: | - cmake --build cmake.output.tinyxml2 --target check -- -j$(nproc) - - build_cmake: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - include: - - use_qt6: On - - os: ubuntu-20.04 - use_qt6: Off - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - env: - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'Off' - run: | - sudo apt-get update - sudo apt-get install libxml2-utils qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser - - # TODO: move latest compiler to separate step - # TODO: bail out on warnings with latest GCC - - name: Set up GCC - uses: egor-tensin/setup-gcc@v1 - if: matrix.os == 'ubuntu-22.04' - with: - version: 13 - platform: x64 - - - name: Select compiler - if: matrix.os == 'ubuntu-22.04' - run: | - echo "CXX=g++-13" >> $GITHUB_ENV - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') && matrix.use_qt6 == 'On' - run: | - sudo apt-get update - # qt6-tools-dev-tools for lprodump - # qt6-l10n-tools for lupdate - sudo apt-get install libxml2-utils qt6-base-dev libqt6charts6-dev qt6-tools-dev qt6-tools-dev-tools qt6-l10n-tools libglx-dev libgl1-mesa-dev - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - # pcre was removed from runner images in November 2022 - brew install coreutils qt@6 pcre - - - name: CMake build on ubuntu (with GUI) - if: contains(matrix.os, 'ubuntu') - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=${{ matrix.use_qt6 }} -DWITH_QCHART=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - cmake --build cmake.output -- -j$(nproc) - - - name: CMake build on macos (with GUI) - if: contains(matrix.os, 'macos') - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DQt6_DIR=$(brew --prefix qt@6)/lib/cmake/Qt6 - cmake --build cmake.output -- -j$(nproc) - - - name: Run CMake test - run: | - cmake --build cmake.output --target check -- -j$(nproc) - - - name: Run CTest - run: | - pushd cmake.output - ctest --output-on-failure -j$(nproc) - - build_uchar: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - brew install coreutils - - - name: Build with Unsigned char - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) CXXFLAGS=-funsigned-char testrunner - - - name: Test with Unsigned char - run: | - ./testrunner TestSymbolDatabase - - build_mathlib: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - brew install coreutils - - - name: Build with TEST_MATHLIB_VALUE - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) CPPFLAGS=-DTEST_MATHLIB_VALUE all - - - name: Test with TEST_MATHLIB_VALUE - run: | - make -j$(nproc) CPPFLAGS=-DTEST_MATHLIB_VALUE check - - check_nonneg: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - # coreutils contains "g++" (default is "c++") and "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - brew install coreutils - - - name: Check syntax with NONNEG - run: | - ls lib/*.cpp | xargs -n 1 -P $(nproc) g++ -fsyntax-only -std=c++0x -Ilib -Iexternals -Iexternals/picojson -Iexternals/simplecpp -Iexternals/tinyxml2 -DNONNEG - - build_qmake: - - strategy: - matrix: - # no longer build with qmake on MacOS as brew might lack pre-built Qt5 packages causing the step to run for hours - os: [ubuntu-20.04, ubuntu-22.04] - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev qtchooser - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - brew install coreutils qt@5 - # expose qmake - brew link qt@5 --force - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Build GUI - run: | - export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - pushd gui - qmake CONFIG+=debug CONFIG+=ccache HAVE_QCHART=yes - make -j$(nproc) - - # TODO: binaries are in a different location on macos - - name: Build and Run GUI tests - if: contains(matrix.os, 'ubuntu') - run: | - export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - pushd gui/test/cppchecklibrarydata - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - ./test-cppchecklibrarydata - popd - pushd gui/test/filelist - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - ./test-filelist - popd - pushd gui/test/projectfile - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - ./test-projectfile - popd - pushd gui/test/translationhandler - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - # TODO: requires X session because of QApplication dependency in translationhandler.cpp - #./test-translationhandler - popd - pushd gui/test/xmlreportv2 - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - ./test-xmlreportv2 - - - name: Generate Qt help file - run: | - pushd gui/help - qhelpgenerator online-help.qhcp -o online-help.qhc - - - name: Build triage - run: | - export PATH="$(brew --prefix)/opt/ccache/libexec:/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - pushd tools/triage - qmake CONFIG+=debug CONFIG+=ccache - make -j$(nproc) - - build: - - strategy: - matrix: - os: [ubuntu-20.04, ubuntu-22.04, macos-12] - fail-fast: false # Prefer quick result - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Install missing software on ubuntu - if: contains(matrix.os, 'ubuntu') - run: | - sudo apt-get update - sudo apt-get install libxml2-utils - - # packages for strict cfg checks - - name: Install missing software on ubuntu 22.04 (cfg) - if: matrix.os == 'ubuntu-22.04' - run: | - sudo apt-get install libcairo2-dev libcurl4-openssl-dev liblua5.3-dev libssl-dev libsqlite3-dev libcppunit-dev libsigc++-2.0-dev libgtk-3-dev libboost-all-dev libwxgtk3.0-gtk3-dev xmlstarlet qtbase5-dev - - # coreutils contains "nproc" - - name: Install missing software on macos - if: contains(matrix.os, 'macos') - run: | - # pcre was removed from runner images in November 2022 - brew install coreutils python3 pcre gnu-sed - - - name: Install missing Python packages - run: | - python3 -m pip install pip --upgrade - python3 -m pip install pytest - python3 -m pip install pytest-timeout - - - name: Build cppcheck - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) HAVE_RULES=yes - - - name: Build test - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) testrunner HAVE_RULES=yes - - - name: Run test - run: | - make -j$(nproc) check HAVE_RULES=yes - - # requires "gnu-sed" installed on macos - - name: Run extra tests - run: | - tools/generate_and_run_more_tests.sh - - # do not use pushd in this step since we go below the working directory - - name: Run test/cli - run: | - cd test/cli - python3 -m pytest -Werror --strict-markers -vv test-*.py - cd ../../.. - ln -s cppcheck 'cpp check' - cd 'cpp check/test/cli' - python3 -m pytest -Werror --strict-markers -vv test-*.py - - - name: Run cfg tests - if: matrix.os != 'ubuntu-22.04' - run: | - make -j$(nproc) checkcfg - - - name: Run cfg tests (strict) - if: matrix.os == 'ubuntu-22.04' - run: | - make -j$(nproc) checkcfg - env: - STRICT: 1 - - - name: Run --dump test - run: | - ./cppcheck test/testpreprocessor.cpp --dump - xmllint --noout test/testpreprocessor.cpp.dump - - - name: Validate - run: | - make -j$(nproc) checkCWEEntries validateXML - - # TODO: move to scriptcheck.yml so these are tested with all Python versions? - - name: Test addons - run: | - ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety addons/test/threadsafety - ./cppcheck --error-exitcode=1 --inline-suppr --addon=threadsafety --std=c++03 addons/test/threadsafety - ./cppcheck --error-exitcode=1 --inline-suppr --addon=misra addons/test/misra/crash*.c - ./cppcheck --error-exitcode=1 --inline-suppr --addon=misra --enable=information addons/test/misra/config*.c - - ./cppcheck --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 addons/test/misra/misra-ctu-*-test.c - pushd addons/test - # We'll force C89 standard to enable an additional verification for - # rules 5.4 and 5.5 which have standard-dependent options. - ../../cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra/misra-test.c --std=c89 --platform=unix64 - python3 ../misra.py -verify misra/misra-test.c.dump - # TODO: do we need to verify something here? - ../../cppcheck --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra/misra-test.h - ../../cppcheck --dump misra/misra-test.cpp - python3 ../misra.py -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_ascii.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_utf8.txt -verify misra/misra-test.cpp.dump - python3 ../misra.py --rule-texts=misra/misra2012_rules_dummy_windows1250.txt -verify misra/misra-test.cpp.dump - ../../cppcheck --addon=misra --enable=style --platform=avr8 --error-exitcode=1 misra/misra-test-avr8.c - ../../cppcheck --dump misc-test.cpp - python3 ../misc.py -verify misc-test.cpp.dump - ../../cppcheck --dump naming_test.c - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump - ../../cppcheck --dump naming_test.cpp - python3 ../naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump - ../../cppcheck --dump namingng_test.c - python3 ../namingng.py --configfile ../naming.json --verify namingng_test.c.dump - - - name: Build democlient - if: matrix.os == 'ubuntu-22.04' - run: | - warnings="-pedantic -Wall -Wextra -Wcast-qual -Wno-deprecated-declarations -Wfloat-equal -Wmissing-declarations -Wmissing-format-attribute -Wno-long-long -Wpacked -Wredundant-decls -Wundef -Wno-shadow -Wno-missing-field-initializers -Wno-missing-braces -Wno-sign-compare -Wno-multichar" - g++ $warnings -c -Ilib -Iexternals/tinyxml2 democlient/democlient.cpp - - selfcheck: - needs: build # wait for all tests to be successful first - - runs-on: ubuntu-22.04 # run on the latest image only - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install qtbase5-dev qttools5-dev libqt5charts5-dev libboost-container-dev - - - name: Self check (build) - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - # compile with verification and ast matchers - make -j$(nproc) -s CPPFLAGS="-DCHECK_INTERNAL" CXXFLAGS="-g -O2 -w -DHAVE_BOOST" MATCHCOMPILER=yes VERIFY=1 - - # TODO: update to Qt6 - - name: Generate UI files - run: | - pushd gui - qmake CONFIG+=debug HAVE_QCHART=yes - make -j$(nproc) compiler_uic_make_all mocables - - - name: Generate triage UI files - run: | - pushd tools/triage - qmake CONFIG+=debug - make -j$(nproc) compiler_uic_make_all mocables - - - name: Self check - run: | - selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" - cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" - ec=0 - - # TODO: add --check-config - - # early exit - if [ $ec -eq 1 ]; then - exit $ec - fi - - # self check simplecpp - ./cppcheck $selfcheck_options externals/simplecpp || ec=1 - # self check lib/cli - mkdir b1 - ./cppcheck $selfcheck_options $cppcheck_options --cppcheck-build-dir=b1 --addon=naming.json cli lib || ec=1 - # check gui with qt settings - mkdir b2 - ./cppcheck $selfcheck_options $cppcheck_options --cppcheck-build-dir=b2 -DQT_VERSION=0x050000 -DQ_MOC_OUTPUT_REVISION=67 -DQT_CHARTS_LIB --library=qt --addon=naming.json -Igui/temp -Igui gui/*.cpp gui/temp/*.cpp || ec=1 - # self check test and tools - ./cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 - # triage - ./cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=67 -DQT_CHARTS_LIB --library=qt -Itools/triage/temp -Igui tools/triage/*.cpp tools/triage/temp/*.cpp || ec=1 - exit $ec diff --git a/.github/workflows/CI-windows.yml b/.github/workflows/CI-windows.yml deleted file mode 100644 index 39d769f1a91..00000000000 --- a/.github/workflows/CI-windows.yml +++ /dev/null @@ -1,207 +0,0 @@ -# Some convenient links: -# - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md -# - -name: CI-windows - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -defaults: - run: - shell: cmd - -# TODO: choose/add a step to bail out on compiler warnings (maybe even the release build) - -jobs: - - build_qt: - strategy: - matrix: - os: [windows-2019, windows-2022] - qt_ver: [5.15.2, 6.6.0] - fail-fast: false - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: Install Qt ${{ matrix.qt_ver }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ matrix.qt_ver }} - modules: 'qtcharts' - cache: true - - - name: Build GUI release (qmake) - if: startsWith(matrix.qt_ver, '5') - run: | - cd gui || exit /b !errorlevel! - qmake HAVE_QCHART=yes || exit /b !errorlevel! - nmake release || exit /b !errorlevel! - env: - CL: /MP - - - name: Deploy GUI - if: startsWith(matrix.qt_ver, '5') - run: | - windeployqt Build\gui || exit /b !errorlevel! - del Build\gui\cppcheck-gui.ilk || exit /b !errorlevel! - del Build\gui\cppcheck-gui.pdb || exit /b !errorlevel! - - - name: Build GUI release (CMake) - if: startsWith(matrix.qt_ver, '6') - run: | - cmake -S . -B build -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On || exit /b !errorlevel! - cmake --build build --target cppcheck-gui || exit /b !errorlevel! - - # TODO: deploy with CMake/Qt6 - - build: - strategy: - matrix: - os: [windows-2019, windows-2022] - config: [debug, release] - fail-fast: false - - runs-on: ${{ matrix.os }} - - env: - # see https://www.pcre.org/original/changelog.txt - PCRE_VERSION: 8.45 - - steps: - - uses: actions/checkout@v3 - - - name: Set up Python 3.12 - if: matrix.config == 'release' - uses: actions/setup-python@v4 - with: - python-version: '3.12' - check-latest: true - - - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 - with: - arch: x64 - - - name: Cache PCRE - id: cache-pcre - uses: actions/cache@v3 - with: - path: | - externals\pcre.h - externals\pcre.lib - externals\pcre64.lib - key: pcre-${{ env.PCRE_VERSION }}-x64-bin-win - - - name: Download PCRE - if: steps.cache-pcre.outputs.cache-hit != 'true' - run: | - curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! - - - name: Install PCRE - if: steps.cache-pcre.outputs.cache-hit != 'true' - run: | - 7z x pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! - cd pcre-%PCRE_VERSION% || exit /b !errorlevel! - cmake . -G "NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -DPCRE_BUILD_PCRECPP=Off -DPCRE_BUILD_TESTS=Off -DPCRE_BUILD_PCREGREP=Off || exit /b !errorlevel! - nmake || exit /b !errorlevel! - copy pcre.h ..\externals || exit /b !errorlevel! - copy pcre.lib ..\externals\pcre64.lib || exit /b !errorlevel! - env: - CL: /MP - - - name: Install missing Python packages - if: matrix.config == 'release' - run: | - python -m pip install pip --upgrade || exit /b !errorlevel! - python -m pip install pytest || exit /b !errorlevel! - python -m pip install pytest-custom_exit_code || exit /b !errorlevel! - python -m pip install pytest-timeout || exit /b !errorlevel! - - - name: Run CMake - if: false # TODO: enable - run: | - cmake -S . -B build -DBUILD_TESTS=On || exit /b !errorlevel! - - - name: Build CLI debug configuration using MSBuild - if: matrix.config == 'debug' - run: | - :: cmake --build build --target check --config Debug || exit /b !errorlevel! - msbuild -m cppcheck.sln /p:Configuration=Debug-PCRE;Platform=x64 -maxcpucount || exit /b !errorlevel! - - - name: Run Debug test - if: matrix.config == 'debug' - run: .\bin\debug\testrunner.exe || exit /b !errorlevel! - - - name: Build CLI release configuration using MSBuild - if: matrix.config == 'release' - run: | - :: cmake --build build --target check --config Release || exit /b !errorlevel! - msbuild -m cppcheck.sln /p:Configuration=Release-PCRE;Platform=x64 -maxcpucount || exit /b !errorlevel! - - - name: Run Release test - if: matrix.config == 'release' - run: .\bin\testrunner.exe || exit /b !errorlevel! - - - name: Run test/cli - if: matrix.config == 'release' - run: | - :: since FILESDIR is not set copy the binary to the root so the addons are found - :: copy .\build\bin\Release\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! - copy .\bin\cppcheck.exe .\cppcheck.exe || exit /b !errorlevel! - copy .\bin\cppcheck-core.dll .\cppcheck-core.dll || exit /b !errorlevel! - cd test/cli || exit /b !errorlevel! - :: python -m pytest -Werror --strict-markers -vv --suppress-no-test-exit-code test-clang-import.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-helloworld.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-inline-suppress.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-more-projects.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-other.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-proj2.py || exit /b !errorlevel! - python -m pytest -Werror --strict-markers -vv test-suppress-syntaxError.py || exit /b !errorlevel! - - - name: Test addons - if: matrix.config == 'release' - run: | - .\cppcheck --addon=threadsafety addons\test\threadsafety || exit /b !errorlevel! - .\cppcheck --addon=threadsafety --std=c++03 addons\test\threadsafety || exit /b !errorlevel! - .\cppcheck --addon=misra --enable=style --inline-suppr --enable=information --error-exitcode=1 addons\test\misra\misra-ctu-*-test.c || exit /b !errorlevel! - cd addons\test - rem We'll force C89 standard to enable an additional verification for - rem rules 5.4 and 5.5 which have standard-dependent options. - ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --inline-suppr misra\misra-test.c --std=c89 --platform=unix64 || exit /b !errorlevel! - python3 ..\misra.py -verify misra\misra-test.c.dump || exit /b !errorlevel! - rem TODO: do we need to verify something here? - ..\..\cppcheck --dump -DDUMMY --suppress=uninitvar --suppress=uninitStructMember --std=c89 misra\misra-test.h || exit /b !errorlevel! - ..\..\cppcheck --dump misra\misra-test.cpp || exit /b !errorlevel! - python3 ..\misra.py -verify misra\misra-test.cpp.dump || exit /b !errorlevel! - python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_ascii.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! - python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_utf8.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! - python3 ..\misra.py --rule-texts=misra\misra2012_rules_dummy_windows1250.txt -verify misra\misra-test.cpp.dump || exit /b !errorlevel! - ..\..\cppcheck --addon=misra --enable=style --platform=avr8 --error-exitcode=1 misra\misra-test-avr8.c || exit /b !errorlevel! - ..\..\cppcheck --dump misc-test.cpp || exit /b !errorlevel! - python3 ..\misc.py -verify misc-test.cpp.dump || exit /b !errorlevel! - ..\..\cppcheck --dump naming_test.c || exit /b !errorlevel! - rem TODO: fix this - does not fail on Linux - rem python3 ..\naming.py --var='[a-z].*' --function='[a-z].*' naming_test.c.dump || exit /b !errorlevel! - ..\..\cppcheck --dump naming_test.cpp || exit /b !errorlevel! - python3 ..\naming.py --var='[a-z].*' --function='[a-z].*' naming_test.cpp.dump || exit /b !errorlevel! - ..\..\cppcheck --dump namingng_test.c || exit /b !errorlevel! - python3 ..\namingng.py --configfile ..\naming.json --verify namingng_test.c.dump || exit /b !errorlevel! - diff --git a/.github/workflows/asan.yml b/.github/workflows/asan.yml deleted file mode 100644 index d0a1c29c32a..00000000000 --- a/.github/workflows/asan.yml +++ /dev/null @@ -1,101 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: address sanitizer - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 5.15.2 - ASAN_OPTIONS: detect_stack_use_after_return=1 - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Set up Python 3.12 - uses: actions/setup-python@v4 - with: - python-version: '3.12' - check-latest: true - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev - - - name: Install clang - run: | - sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 17 - - - name: Install Qt ${{ env.QT_VERSION }} - if: false - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - # TODO: disable all warnings - - name: CMake - run: | - cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=Off -DWITH_QCHART=Off -DUSE_MATCHCOMPILER=Verify -DANALYZE_ADDRESS=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=Off -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - env: - CC: clang-17 - CXX: clang++-17 - - - name: Build cppcheck - run: | - cmake --build cmake.output --target cppcheck -- -j $(nproc) - - - name: Build test - run: | - cmake --build cmake.output --target testrunner -- -j $(nproc) - - - name: Run tests - run: ./cmake.output/bin/testrunner - - - name: Generate dependencies - if: false - run: | - # make sure auto-generated GUI files exist - make -C cmake.output autogen - make -C cmake.output gui-build-deps triage-build-ui-deps - - # TODO: this is currently way too slow (~60 minutes) to enable it - # TODO: only fail the step on sanitizer issues - since we use processes it will only fail the underlying process which will result in an cppcheckError - - name: Self check - if: false - run: | - selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" - cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" - ec=0 - ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json cli lib || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 - exit $ec diff --git a/.github/workflows/buildman.yml b/.github/workflows/buildman.yml deleted file mode 100644 index cb7fc655bee..00000000000 --- a/.github/workflows/buildman.yml +++ /dev/null @@ -1,58 +0,0 @@ -name: Build manual - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - convert_via_pandoc: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - - run: | - mkdir output - - - uses: docker://pandoc/latex:2.9 - with: - args: --output=output/manual.html man/manual.md - - - uses: docker://pandoc/latex:2.9 - with: - args: --output=output/manual.pdf man/manual.md - - - uses: docker://pandoc/latex:2.9 - with: - args: --output=output/manual-premium.pdf man/manual-premium.md - - - uses: actions/upload-artifact@v3 - with: - name: output - path: output - - manpage: - runs-on: ubuntu-22.04 - steps: - - uses: actions/checkout@v3 - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install -y xsltproc docbook-xsl - - - name: build manpage - run: | - make man - - - uses: actions/upload-artifact@v3 - with: - name: cppcheck.1 - path: cppcheck.1 diff --git a/.github/workflows/cifuzz.yml b/.github/workflows/cifuzz.yml deleted file mode 100644 index 19c40de61c9..00000000000 --- a/.github/workflows/cifuzz.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: CIFuzz -on: [pull_request] -jobs: - Fuzzing: - runs-on: ubuntu-latest - steps: - - name: Build Fuzzers - id: build - uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master - with: - oss-fuzz-project-name: 'cppcheck' - dry-run: false - language: c++ - - name: Run Fuzzers - uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master - with: - oss-fuzz-project-name: 'cppcheck' - fuzz-seconds: 300 - dry-run: false - language: c++ - - name: Upload Crash - uses: actions/upload-artifact@v3 - if: failure() && steps.build.outcome == 'success' - with: - name: artifacts - path: ./out/artifacts diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml deleted file mode 100644 index 59d40952009..00000000000 --- a/.github/workflows/clang-tidy.yml +++ /dev/null @@ -1,73 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: clang-tidy - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 6.6.0 - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software - run: | - sudo apt-get update - sudo apt-get install -y cmake make - sudo apt-get install -y libpcre3-dev - sudo apt-get install -y libffi7 # work around missing dependency for Qt install step - - - name: Install clang - run: | - sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 17 - sudo apt-get install -y clang-tidy-17 - - - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - - name: Verify clang-tidy configuration - run: | - clang-tidy-17 --verify-config - - - name: Prepare CMake - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DUSE_QT6=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCPPCHK_GLIBCXX_DEBUG=Off - env: - CC: clang-17 - CXX: clang++-17 - - - name: Prepare CMake dependencies - run: | - # make sure the precompiled headers exist - make -C cmake.output/cli cmake_pch.hxx.pch - make -C cmake.output/gui cmake_pch.hxx.pch - make -C cmake.output/lib cmake_pch.hxx.pch - make -C cmake.output/test cmake_pch.hxx.pch - # make sure the auto-generated GUI sources exist - make -C cmake.output autogen - - - name: Clang-Tidy - run: | - cmake --build cmake.output --target run-clang-tidy 2> /dev/null diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml deleted file mode 100644 index f486cf9e24f..00000000000 --- a/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,54 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - security-events: write - -jobs: - analyze: - name: Analyze - runs-on: ubuntu-22.04 - - strategy: - fail-fast: false - matrix: - # Override automatic language detection by changing the below list - # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] - language: ['cpp', 'python'] - # Learn more... - # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install libxml2-utils - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - languages: ${{ matrix.language }} - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - # queries: ./path/to/local/query, your-org/your-repo/queries@main - setup-python-dependencies: false - - - run: | - make -j$(nproc) HAVE_RULES=yes cppcheck - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml deleted file mode 100644 index 5018f2c3b8a..00000000000 --- a/.github/workflows/coverage.yml +++ /dev/null @@ -1,68 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: Coverage - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ runner.os }} - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install libxml2-utils lcov - - - name: Install missing Python packages on ubuntu - run: | - python -m pip install pip --upgrade - python -m pip install lcov_cobertura - - - name: Compile instrumented - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) all CXXFLAGS="-g -fprofile-arcs -ftest-coverage" HAVE_RULES=yes - - - name: Run instrumented tests - run: | - ./testrunner - test/cfg/runtests.sh - - - name: Generate coverage report - run: | - gcov lib/*.cpp -o lib/ - lcov --directory ./ --capture --output-file lcov_tmp.info -b ./ - lcov --extract lcov_tmp.info "$(pwd)/*" --output-file lcov.info - genhtml lcov.info -o coverage_report --frame --legend --demangle-cpp - - - uses: actions/upload-artifact@v3 - with: - name: Coverage results - path: coverage_report - - - uses: codecov/codecov-action@v3 - with: - # token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos - # file: ./coverage.xml # optional - flags: unittests # optional - name: ${{ github.repository }} # optional - fail_ci_if_error: true # optional (default = false): diff --git a/.github/workflows/cppcheck-premium.yml b/.github/workflows/cppcheck-premium.yml deleted file mode 100644 index 11d6f3b8cac..00000000000 --- a/.github/workflows/cppcheck-premium.yml +++ /dev/null @@ -1,43 +0,0 @@ - -name: cppcheck-premium - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - - build: - runs-on: ubuntu-22.04 # run on the latest image only - - env: - PREMIUM_VERSION: devdrop-20231105 - - steps: - - uses: actions/checkout@v3 - - - name: Download cppcheckpremium - run: | - wget https://files.cppchecksolutions.com/cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz - tar xzf cppcheckpremium-${{ env.PREMIUM_VERSION }}-amd64.tar.gz - - - name: Generate a license file - run: | - echo cppcheck > cppcheck.lic - echo 231231 >> cppcheck.lic - echo 80000 >> cppcheck.lic - echo 57e08c39523ab54d >> cppcheck.lic - echo path:lib >> cppcheck.lic - - - name: Check - run: | - cppcheckpremium-${{ env.PREMIUM_VERSION }}/premiumaddon --check-loc-license cppcheck.lic > cppcheck-premium-loc - cppcheckpremium-${{ env.PREMIUM_VERSION }}/cppcheck -j$(nproc) -D__GNUC__ -D__CPPCHECK__ --suppressions-list=cppcheckpremium-suppressions --platform=unix64 --enable=style --premium=misra-c++-2008 --premium=cert-c++-2016 --error-exitcode=1 lib diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml deleted file mode 100644 index 3ae9ceb200f..00000000000 --- a/.github/workflows/format.yml +++ /dev/null @@ -1,47 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: format - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - - - name: Cache uncrustify - uses: actions/cache@v3 - id: cache-uncrustify - with: - path: | - ~/uncrustify - key: ${{ runner.os }}-uncrustify - - - name: build uncrustify - if: steps.cache-uncrustify.outputs.cache-hit != 'true' - run: | - wget https://github.com/uncrustify/uncrustify/archive/refs/tags/uncrustify-0.72.0.tar.gz - tar xzvf uncrustify-0.72.0.tar.gz && cd uncrustify-uncrustify-0.72.0 - cmake -S . -B build -DCMAKE_BUILD_TYPE=Release - cmake --build build -- -j$(nproc) -s - mkdir ~/uncrustify - cd build && cp uncrustify ~/uncrustify/ - - - name: Uncrustify check - run: | - ~/uncrustify/uncrustify -c .uncrustify.cfg -l CPP --no-backup --replace */*.cpp */*.h - git diff - git diff | diff - /dev/null &> /dev/null diff --git a/.github/workflows/iwyu.yml b/.github/workflows/iwyu.yml deleted file mode 100644 index dd6d10278ec..00000000000 --- a/.github/workflows/iwyu.yml +++ /dev/null @@ -1,165 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: include-what-you-use - -on: workflow_dispatch - -permissions: - contents: read - -jobs: - iwyu: - - strategy: - matrix: - image: ["fedora:latest"] # "debian:unstable" / "archlinux:latest" - - runs-on: ubuntu-22.04 - - container: - image: "fedora:latest" - - steps: - - uses: actions/checkout@v3 - - # TODO: the necessary packages are excessive - mostly because of Qt - use a pre-built image - - name: Install missing software on debian/ubuntu - if: contains(matrix.image, 'debian') - run: | - apt-get update - apt-get install -y cmake g++ make libpcre3-dev - apt-get install -y qtbase5-dev qttools5-dev libqt5charts5-dev - apt-get install -y wget iwyu - ln -s ../x86_64-linux-gnu/qt5 /usr/include/qt - - - name: Install missing software on archlinux - if: contains(matrix.image, 'archlinux') - run: | - set -x - pacman -Sy - pacman -S cmake make gcc qt5-base qt5-tools qt5-charts pcre wget --noconfirm - pacman-key --init - pacman-key --recv-key 3056513887B78AEB --keyserver keyserver.ubuntu.com - pacman-key --lsign-key 3056513887B78AEB - pacman -U 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-keyring.pkg.tar.zst' 'https://cdn-mirror.chaotic.cx/chaotic-aur/chaotic-mirrorlist.pkg.tar.zst' --noconfirm - echo "[chaotic-aur]" >> /etc/pacman.conf - echo "Include = /etc/pacman.d/chaotic-mirrorlist" >> /etc/pacman.conf - pacman -Sy - pacman -S include-what-you-use --noconfirm - ln -s iwyu-tool /usr/sbin/iwyu_tool - - # TODO: the necessary packages are excessive - mostly because of Qt - use a pre-built image - - name: Install missing software on Fedora - if: contains(matrix.image, 'fedora') - run: | - dnf install -y cmake gcc-c++ qt5-qtbase-devel qt5-linguist qt5-qttools-devel qt5-qtcharts-devel pcre-devel - dnf install -y wget iwyu - ln -s iwyu_tool.py /usr/bin/iwyu_tool - ln -s qt5 /usr/include/qt - - # TODO: switch to Qt 6 after we enabled the Qt mappings again - - name: Prepare CMake - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off - - - name: Prepare CMake dependencies - run: | - # make sure the precompiled headers exist - #make -C cmake.output/cli cmake_pch.hxx.pch - #make -C cmake.output/gui cmake_pch.hxx.pch - #make -C cmake.output/lib cmake_pch.hxx.pch - #make -C cmake.output/test cmake_pch.hxx.pch - # make sure the auto-generated GUI sources exist - make -C cmake.output autogen - # make sure the auto-generated GUI dependencies exist - make -C cmake.output gui-build-deps - make -C cmake.output triage-build-ui-deps - - - name: Build Qt mappings - run: | - wget https://raw.githubusercontent.com/include-what-you-use/include-what-you-use/master/mapgen/iwyu-mapgen-qt.py - python3 iwyu-mapgen-qt.py /usr/include/qt/ > qt5.imp - - - name: iwyu_tool - run: | - PWD=$(pwd) - iwyu_tool -p cmake.output -j $(nproc) -- -w -Xiwyu --max_line_length=1024 -Xiwyu --comment_style=long -Xiwyu --quoted_includes_first -Xiwyu --update_comments -Xiwyu --mapping_file=$PWD/qt5.imp -I/usr/lib/clang/17/include > iwyu.log - - - uses: actions/upload-artifact@v3 - if: success() || failure() - with: - name: Compilation Database - path: ./cmake.output/compile_commands.json - - - uses: actions/upload-artifact@v3 - if: success() || failure() - with: - name: Qt Mappings - path: ./qt5.imp - - - uses: actions/upload-artifact@v3 - if: success() || failure() - with: - name: Logs (include-what-you-use) - path: ./*.log - - clang-include-cleaner: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 5.15.2 - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software - run: | - sudo apt-get update - sudo apt-get install -y cmake make - sudo apt-get install -y libpcre3-dev - sudo apt-get install -y libffi7 # work around missing dependency for Qt install step - - - name: Install clang - run: | - sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 17 - sudo apt-get install -y clang-tools-17 - - - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - - name: Prepare CMake - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DCMAKE_BUILD_TYPE=Release -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=On -DBUILD_TESTS=On -DBUILD_GUI=On -DWITH_QCHART=On -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCPPCHK_GLIBCXX_DEBUG=Off -DUSE_MATCHCOMPILER=Off - env: - CC: clang-17 - CXX: clang++-17 - - - name: Prepare CMake dependencies - run: | - # make sure the precompiled headers exist - #make -C cmake.output/cli cmake_pch.hxx.pch - #make -C cmake.output/gui cmake_pch.hxx.pch - #make -C cmake.output/lib cmake_pch.hxx.pch - #make -C cmake.output/test cmake_pch.hxx.pch - # make sure the auto-generated GUI sources exist - make -C cmake.output autogen - # make sure the auto-generated GUI dependencies exist - make -C cmake.output gui-build-deps - - - name: clang-include-cleaner - run: | - # TODO: run multi-threaded - find $PWD/cli $PWD/lib $PWD/test -maxdepth 1 -name "*.cpp" | xargs -t -n 1 clang-include-cleaner-17 --print=changes --extra-arg=-w -p cmake.output > clang-include-cleaner.log 2>&1 - - - uses: actions/upload-artifact@v3 - with: - name: Logs (clang-include-cleaner) - path: ./*.log \ No newline at end of file diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml deleted file mode 100644 index f10a67eaed2..00000000000 --- a/.github/workflows/release-windows.yml +++ /dev/null @@ -1,169 +0,0 @@ -# Some convenient links: -# - https://github.com/actions/virtual-environments/blob/master/images/win/Windows2019-Readme.md -# - -name: release-windows - -on: - push: - tags: - - '2.*' - schedule: - - cron: '0 0 * * *' - workflow_dispatch: - -permissions: - contents: read - -defaults: - run: - shell: cmd - -jobs: - - build: - - runs-on: windows-2022 - - env: - # see https://www.pcre.org/original/changelog.txt - PCRE_VERSION: 8.45 - QT_VERSION: 5.15.2 - - steps: - - uses: actions/checkout@v3 - - - name: Set up Visual Studio environment - uses: ilammy/msvc-dev-cmd@v1 - - - name: Cache PCRE - id: cache-pcre - uses: actions/cache@v3 - with: - path: | - externals\pcre.h - externals\pcre64.lib - key: pcre-${{ env.PCRE_VERSION }}-bin-x64-win-release-job - - - name: Download PCRE - if: steps.cache-pcre.outputs.cache-hit != 'true' - run: | - curl -fsSL https://github.com/pfultz2/pcre/archive/refs/tags/%PCRE_VERSION%.zip -o pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! - - - name: Install PCRE - if: steps.cache-pcre.outputs.cache-hit != 'true' - run: | - 7z x pcre-%PCRE_VERSION%.zip || exit /b !errorlevel! - cd pcre-%PCRE_VERSION% || exit /b !errorlevel! - cmake . -G "Visual Studio 17 2022" -A x64 -DPCRE_BUILD_PCRECPP=OFF -DPCRE_BUILD_PCREGREP=OFF -DPCRE_BUILD_TESTS=OFF || exit /b !errorlevel! - msbuild -m PCRE.sln -p:Configuration=Release -p:Platform=x64 || exit /b !errorlevel! - copy pcre.h ..\externals || exit /b !errorlevel! - copy Release\pcre.lib ..\externals\pcre64.lib || exit /b !errorlevel! - - # available modules: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-modules - # available tools: https://github.com/miurahr/aqtinstall/blob/master/docs/getting_started.rst#installing-tools - - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - tools: 'tools_opensslv3_x64' - cache: true - - - name: Create .qm - run: | - cd gui || exit /b !errorlevel! - lupdate gui.pro -no-obsolete || exit /b !errorlevel! - lrelease gui.pro -removeidentical || exit /b !errorlevel! - - - name: Matchcompiler - run: python tools\matchcompiler.py --write-dir lib || exit /b !errorlevel! - - - name: Build x64 release GUI - run: | - cd gui || exit /b !errorlevel! - qmake HAVE_QCHART=yes || exit /b !errorlevel! - nmake release || exit /b !errorlevel! - env: - CL: /MP - - - name: Deploy app - run: | - windeployqt Build\gui || exit /b !errorlevel! - del Build\gui\cppcheck-gui.ilk || exit /b !errorlevel! - del Build\gui\cppcheck-gui.pdb || exit /b !errorlevel! - - # TODO: build with boost enabled - - name: Build CLI x64 release configuration using MSBuild - run: msbuild -m cppcheck.sln -t:cli -p:Configuration=Release-PCRE -p:Platform=x64 || exit /b !errorlevel! - - - name: Compile misra.py executable - run: | - pip install -U pyinstaller || exit /b !errorlevel! - cd addons || exit /b !errorlevel! - pyinstaller --hidden-import xml --hidden-import xml.etree --hidden-import xml.etree.ElementTree misra.py || exit /b !errorlevel! - del *.spec || exit /b !errorlevel! - - - name: Collect files - run: | - move Build\gui win_installer\files || exit /b !errorlevel! - mkdir win_installer\files\addons || exit /b !errorlevel! - copy addons\*.* win_installer\files\addons || exit /b !errorlevel! - copy addons\dist\misra\*.* win_installer\files\addons || exit /b !errorlevel! - mkdir win_installer\files\cfg || exit /b !errorlevel! - copy cfg\*.cfg win_installer\files\cfg || exit /b !errorlevel! - :: "platforms" is a folder used by Qt as well so it already exists - :: mkdir win_installer\files\platforms || exit /b !errorlevel! - copy platforms\*.xml win_installer\files\platforms || exit /b !errorlevel! - copy bin\cppcheck.exe win_installer\files || exit /b !errorlevel! - copy bin\cppcheck-core.dll win_installer\files || exit /b !errorlevel! - mkdir win_installer\files\help || exit /b !errorlevel! - xcopy /s gui\help win_installer\files\help || exit /b !errorlevel! - del win_installer\files\translations\*.qm || exit /b !errorlevel! - move gui\*.qm win_installer\files\translations || exit /b !errorlevel! - :: copy libcrypto-3-x64.dll and libssl-3-x64.dll - copy %RUNNER_WORKSPACE%\Qt\Tools\OpenSSLv3\Win_x64\bin\lib*.dll win_installer\files || exit /b !errorlevel! - - - name: Build Installer - run: | - cd win_installer || exit /b !errorlevel! - REM Read ProductVersion - for /f "tokens=4 delims= " %%a in ('find "ProductVersion" productInfo.wxi') do set PRODUCTVER=%%a - REM Remove double quotes - set PRODUCTVER=%PRODUCTVER:"=% - echo ProductVersion="%PRODUCTVER%" || exit /b !errorlevel! - msbuild -m cppcheck.wixproj -p:Platform=x64,ProductVersion=%PRODUCTVER%.${{ github.run_number }} || exit /b !errorlevel! - - - uses: actions/upload-artifact@v3 - with: - name: installer - path: win_installer/Build/ - - - uses: actions/upload-artifact@v3 - with: - name: deploy - path: win_installer\files - - - name: Clean up deploy - run: | - del win_installer\files\addons\*.dll || exit /b !errorlevel! - del win_installer\files\addons\*.pyd || exit /b !errorlevel! - del win_installer\files\addons\base_library.zip || exit /b !errorlevel! - rmdir /s /q win_installer\files\bearer || exit /b !errorlevel! - rmdir /s /q win_installer\files\help || exit /b !errorlevel! - rmdir /s /q win_installer\files\iconengines || exit /b !errorlevel! - rmdir /s /q win_installer\files\imageformats || exit /b !errorlevel! - rmdir /s /q win_installer\files\printsupport || exit /b !errorlevel! - rmdir /s /q win_installer\files\sqldrivers || exit /b !errorlevel! - ren win_installer\files\translations lang || exit /b !errorlevel! - del win_installer\files\d3dcompiler_47.dll || exit /b !errorlevel! - del win_installer\files\libEGL.dll || exit /b !errorlevel! - del win_installer\files\libGLESv2.dll || exit /b !errorlevel! - del win_installer\files\opengl32sw.dll || exit /b !errorlevel! - del win_installer\files\Qt5Svg.dll || exit /b !errorlevel! - del win_installer\files\vc_redist.x64.exe || exit /b !errorlevel! - - - uses: actions/upload-artifact@v3 - with: - name: portable - path: win_installer\files diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml deleted file mode 100644 index 1850d855b4a..00000000000 --- a/.github/workflows/scriptcheck.yml +++ /dev/null @@ -1,200 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: scriptcheck - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - # 'ubuntu-22.04' removes Python 2.7, 3.6 and 3.6 so keep the previous LTS version - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ runner.os }} - - - name: Cache Cppcheck - uses: actions/cache@v3 - with: - path: cppcheck - key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} - - - name: build cppcheck - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - make -j$(nproc) -s CXXFLAGS="-w" - strip -s ./cppcheck - - scriptcheck: - - needs: build - # 'ubuntu-22.04' removes Python 2.7, 3.5 and 3.6 so keep the previous LTS version - # 'ubutunu-20.04' no longer works on 2.7 - TODO: re-added in a different way or remove support for it? - runs-on: ubuntu-20.04 - strategy: - matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] - include: - - python-version: '3.12' - python-latest: true - - fail-fast: false - - steps: - - uses: actions/checkout@v3 - - - name: Restore Cppcheck - uses: actions/cache@v3 - with: - path: cppcheck - key: ${{ runner.os }}-scriptcheck-cppcheck-${{ github.sha }} - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - check-latest: true - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install tidy libxml2-utils - - - name: Install missing software on ubuntu (Python 2) - if: matrix.python-version == '2.7' - run: | - python -m pip install pip --upgrade - python -m pip install pathlib - python -m pip install pytest - python -m pip install pygments - - - name: Install missing software on ubuntu (Python 3) - if: matrix.python-version != '2.7' - run: | - # shellcheck cannot be installed via pip - # ERROR: Could not find a version that satisfies the requirement shellcheck (from versions: none) - # ERROR: No matching distribution found for shellcheck - sudo apt-get install shellcheck - python -m pip install pip --upgrade - python -m pip install natsort - python -m pip install pexpect - python -m pip install pylint - python -m pip install unittest2 - python -m pip install pytest - python -m pip install pygments - python -m pip install requests - python -m pip install psutil - - - name: run Shellcheck - if: matrix.python-latest - run: | - find . -name "*.sh" | xargs shellcheck --exclude SC2002,SC2013,SC2034,SC2035,SC2043,SC2046,SC2086,SC2089,SC2090,SC2129,SC2211,SC2231 - - - name: run pylint - if: matrix.python-latest - run: | - echo "FIXME pylint is disabled for now because it fails to import files:" - echo "FIXME addons/runaddon.py:1:0: E0401: Unable to import 'cppcheckdata' (import-error)" - echo "FIXME addons/runaddon.py:1:0: E0401: Unable to import 'cppcheck' (import-error)" - # pylint --rcfile=pylintrc_travis --jobs $(nproc) addons/*.py htmlreport/cppcheck-htmlreport htmlreport/*.py tools/*.py - - - name: check .json files - if: matrix.python-latest - run: | - find . -name '*.json' | xargs -n 1 python -m json.tool > /dev/null - - - name: Validate - if: matrix.python-latest - run: | - make -j$(nproc) validateCFG validatePlatforms validateRules - - - name: check python syntax - if: matrix.python-version != '2.7' - run: | - python -m py_compile addons/*.py - python -m py_compile htmlreport/cppcheck-htmlreport - python -m py_compile htmlreport/*.py - python -m py_compile tools/*.py - - - name: compile addons - run: | - python -m compileall ./addons - - - name: test matchcompiler - run: | - python tools/test_matchcompiler.py - - # we cannot specify -Werror since xml/etree/ElementTree.py in Python 3.9/3.10 contains an unclosed file - - name: test addons - if: matrix.python-version == '3.9' || matrix.python-version == '3.10' - run: | - python -m pytest --strict-markers -vv addons/test/test-*.py - env: - PYTHONPATH: ./addons - - - name: test addons - if: matrix.python-version != '3.9' && matrix.python-version != '3.10' - run: | - python -m pytest -Werror --strict-markers -vv addons/test/test-*.py - env: - PYTHONPATH: ./addons - - - name: test htmlreport - run: | - htmlreport/test_htmlreport.py - cd htmlreport - ./check.sh - - - name: test reduce - run: | - python -m pytest -Werror --strict-markers -vv tools/test_reduce.py - env: - PYTHONPATH: ./tools - - - name: test donate_cpu_lib - if: matrix.python-version != '2.7' - run: | - python -m pytest -Werror --strict-markers -vv tools/test_donate_cpu_lib.py - env: - PYTHONPATH: ./tools - - - name: test donate_cpu_server - if: matrix.python-version != '2.7' - run: | - python -m pytest -Werror --strict-markers -vv tools/test_donate_cpu_server.py - env: - PYTHONPATH: ./tools - - dmake: - strategy: - matrix: - os: [ubuntu-22.04, macos-12, windows-2022] - fail-fast: false - - runs-on: ${{ matrix.os }} - - steps: - - uses: actions/checkout@v3 - - - name: run dmake - run: | - make -j2 CXXFLAGS="-w" run-dmake - - - name: check diff - run: | - git diff --exit-code diff --git a/.github/workflows/selfcheck.yml b/.github/workflows/selfcheck.yml deleted file mode 100644 index 912c1771766..00000000000 --- a/.github/workflows/selfcheck.yml +++ /dev/null @@ -1,135 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: selfcheck - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 6.6.0 - - steps: - - uses: actions/checkout@v3 - - - name: Install missing software - run: | - sudo apt-get update - sudo apt-get install libboost-container-dev - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ runner.os }} - - - name: Install missing software - run: | - sudo apt-get update - sudo apt-get install clang-14 valgrind - - - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - # TODO: cache this - perform same build as for the other self check - - name: Self check (build) - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - # valgrind cannot handle DWARF 5 yet so force version 4 - # work around performance regression with -inline-deferral - make -j$(nproc) -s CXXFLAGS="-O2 -w -DHAVE_BOOST -gdwarf-4 -mllvm -inline-deferral" MATCHCOMPILER=yes - env: - CC: clang-14 - CXX: clang++-14 - - - name: CMake - run: | - cmake -S . -B cmake.output -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On - - - name: Generate dependencies - run: | - # make sure the precompiled headers exist - make -C cmake.output lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx - make -C cmake.output test/CMakeFiles/testrunner.dir/cmake_pch.hxx.cxx - # make sure auto-generated GUI files exist - make -C cmake.output autogen - make -C cmake.output gui-build-deps - - # TODO: find a way to report unmatched suppressions without need to add information checks - - name: Self check (unusedFunction) - if: false # TODO: fails with preprocessorErrorDirective - see #10667 - run: | - ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --enable=unusedFunction --exception-handling -rp=. --project=cmake.output/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr - env: - DISABLE_VALUEFLOW: 1 - UNUSEDFUNCTION_ONLY: 1 - - # the following steps are duplicated from above since setting up the build node in a parallel step takes longer than the actual steps - - name: CMake (no test) - run: | - cmake -S . -B cmake.output.notest -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=Off -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On - - - name: Generate dependencies (no test) - run: | - # make sure the precompiled headers exist - make -C cmake.output.notest lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx - # make sure auto-generated GUI files exist - make -C cmake.output.notest autogen - make -C cmake.output.notest gui-build-deps - - # TODO: find a way to report unmatched suppressions without need to add information checks - - name: Self check (unusedFunction / no test) - run: | - ./cppcheck -q --template=selfcheck --error-exitcode=1 --library=cppcheck-lib --library=qt -D__CPPCHECK__ -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.notest/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr - env: - DISABLE_VALUEFLOW: 1 - UNUSEDFUNCTION_ONLY: 1 - - - name: Fetch corpus - run: | - wget https://github.com/danmar/cppcheck/archive/refs/tags/2.8.tar.gz - tar xvf 2.8.tar.gz - - - name: CMake (corpus / no test) - run: | - cmake -S cppcheck-2.8 -B cmake.output.corpus -G "Unix Makefiles" -DHAVE_RULES=On -DBUILD_TESTS=Off -DBUILD_GUI=ON -DUSE_QT6=On -DWITH_QCHART=ON -DENABLE_CHECK_INTERNAL=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On - - - name: Generate dependencies (corpus) - run: | - # make sure the precompiled headers exist - make -C cmake.output.notest lib/CMakeFiles/cppcheck-core.dir/cmake_pch.hxx.cxx - # make sure auto-generated GUI files exist - make -C cmake.output.corpus autogen - make -C cmake.output.corpus gui-build-deps - - # TODO: find a way to report unmatched suppressions without need to add information checks - - name: Self check (unusedFunction / corpus / no test / callgrind) - run: | - # TODO: fix -rp so the suppressions actually work - valgrind --tool=callgrind ./cppcheck --template=selfcheck --error-exitcode=0 --library=cppcheck-lib --library=qt -D__GNUC__ -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --enable=unusedFunction --exception-handling -rp=. --project=cmake.output.corpus/compile_commands.json --suppressions-list=.selfcheck_unused_suppressions --inline-suppr 2>callgrind.log || (cat callgrind.log && false) - cat callgrind.log - callgrind_annotate --auto=no > callgrind.annotated.log - head -50 callgrind.annotated.log - env: - DISABLE_VALUEFLOW: 1 - - - uses: actions/upload-artifact@v3 - with: - name: Callgrind Output - path: ./callgrind.* diff --git a/.github/workflows/tsan.yml b/.github/workflows/tsan.yml deleted file mode 100644 index fdb17dae7a1..00000000000 --- a/.github/workflows/tsan.yml +++ /dev/null @@ -1,100 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: thread sanitizer - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 5.15.2 - TSAN_OPTIONS: halt_on_error=1 - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Set up Python 3.12 - uses: actions/setup-python@v4 - with: - python-version: '3.12' - check-latest: true - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev - - - name: Install clang - run: | - sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 17 - - - name: Install Qt ${{ env.QT_VERSION }} - if: false - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - - name: CMake - run: | - cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=Off -DWITH_QCHART=Off -DUSE_MATCHCOMPILER=Verify -DANALYZE_THREAD=On -DUSE_THREADS=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=Off -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - env: - CC: clang-17 - CXX: clang++-17 - - - name: Build cppcheck - run: | - cmake --build cmake.output --target cppcheck -- -j $(nproc) - - - name: Build test - run: | - cmake --build cmake.output --target testrunner -- -j $(nproc) - - - name: Run tests - run: ./cmake.output/bin/testrunner - - - name: Generate dependencies - if: false - run: | - # make sure auto-generated GUI files exist - make -C cmake.output autogen - make -C cmake.output gui-build-deps triage-build-ui-deps - - # TODO: disabled for now as it takes around 40 minutes to finish - # set --error-exitcode=0 so we only fail on sanitizer issues - since it uses threads for execution it will exit the whole process on the first issue - - name: Self check - if: false - run: | - selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=0 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" - cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" - ec=0 - ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json -DCHECK_INTERNAL cli lib || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 - exit $ec diff --git a/.github/workflows/ubsan.yml b/.github/workflows/ubsan.yml deleted file mode 100644 index 65444898737..00000000000 --- a/.github/workflows/ubsan.yml +++ /dev/null @@ -1,97 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: undefined behaviour sanitizers - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - env: - QT_VERSION: 5.15.2 - UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1:report_error_type=1 - # TODO: figure out why there are cache misses with PCH enabled - CCACHE_SLOPPINESS: pch_defines,time_macros - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ github.job }}-${{ matrix.os }} - - - name: Set up Python 3.12 - uses: actions/setup-python@v4 - with: - python-version: '3.12' - check-latest: true - - - name: Install missing software on ubuntu - run: | - sudo apt-get update - sudo apt-get install -y cmake make libpcre3-dev libboost-container-dev - - - name: Install clang - run: | - sudo apt-get purge --auto-remove llvm python3-lldb-14 llvm-14 - wget https://apt.llvm.org/llvm.sh - chmod +x llvm.sh - sudo ./llvm.sh 17 - - - name: Install Qt ${{ env.QT_VERSION }} - uses: jurplel/install-qt-action@v3 - with: - version: ${{ env.QT_VERSION }} - modules: 'qtcharts' - cache: true - - # TODO: disable warnings - - name: CMake - run: | - cmake -S . -B cmake.output -DCMAKE_BUILD_TYPE=RelWithDebInfo -DHAVE_RULES=On -DBUILD_TESTS=On -DBUILD_GUI=ON -DWITH_QCHART=ON -DUSE_MATCHCOMPILER=Verify -DANALYZE_UNDEFINED=On -DENABLE_CHECK_INTERNAL=On -DUSE_BOOST=On -DCPPCHK_GLIBCXX_DEBUG=Off -DCMAKE_DISABLE_PRECOMPILE_HEADERS=On -DCMAKE_GLOBAL_AUTOGEN_TARGET=On -DDISABLE_DMAKE=On -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache - env: - CC: clang-17 - CXX: clang++-17 - - - name: Build cppcheck - run: | - cmake --build cmake.output --target cppcheck -- -j $(nproc) - - - name: Build test - run: | - cmake --build cmake.output --target testrunner -- -j $(nproc) - - - name: Run tests - run: ./cmake.output/bin/testrunner - - - name: Generate dependencies - run: | - # make sure auto-generated GUI files exist - make -C cmake.output autogen - make -C cmake.output gui-build-deps triage-build-ui-deps - - # TODO: only fail the step on sanitizer issues - since we use processes it will only fail the underlying process which will result in an cppcheckError - - name: Self check - run: | - selfcheck_options="-q -j$(nproc) --std=c++11 --template=selfcheck --showtime=top5_summary -D__GNUC__ --error-exitcode=1 --inline-suppr --suppressions-list=.selfcheck_suppressions --library=gnu --inconclusive --enable=style,performance,portability,warning,missingInclude,internal --exception-handling --debug-warnings --check-level=exhaustive" - cppcheck_options="-D__CPPCHECK__ -DCHECK_INTERNAL -DHAVE_RULES --library=cppcheck-lib -Ilib -Iexternals/simplecpp/ -Iexternals/tinyxml2" - ec=0 - ./cmake.output/bin/cppcheck $selfcheck_options externals/simplecpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options --addon=naming.json cli lib || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQT_VERSION=0x060000 -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt --addon=naming.json -Icmake.output/gui -Igui gui/*.cpp cmake.output/gui/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -Icli test/*.cpp tools/*.cpp || ec=1 - ./cmake.output/bin/cppcheck $selfcheck_options $cppcheck_options -DQ_MOC_OUTPUT_REVISION=68 -DQT_CHARTS_LIB --library=qt -Icmake.output/tools/triage -Igui tools/triage/*.cpp cmake.output/tools/triage/*.cpp || ec=1 - exit $ec diff --git a/.github/workflows/valgrind.yml b/.github/workflows/valgrind.yml deleted file mode 100644 index 97f875d5205..00000000000 --- a/.github/workflows/valgrind.yml +++ /dev/null @@ -1,61 +0,0 @@ -# Syntax reference https://help.github.com/en/actions/reference/workflow-syntax-for-github-actions -# Environment reference https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners -name: valgrind - -on: - push: - branches: - - 'main' - - 'releases/**' - tags: - - '2.*' - pull_request: - -permissions: - contents: read - -jobs: - build: - - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v3 - - - name: ccache - uses: hendrikmuhs/ccache-action@v1.2 - with: - key: ${{ github.workflow }}-${{ runner.os }} - - - name: Install missing software - run: | - sudo apt-get update - sudo apt-get install libxml2-utils - sudo apt-get install valgrind - sudo apt-get install libboost-container-dev - sudo apt-get install debuginfod - - - name: Build cppcheck - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - CXXFLAGS="-O1 -g -w -DHAVE_BOOST" make -j$(nproc) HAVE_RULES=yes MATCHCOMPILER=yes - - - name: Build test - run: | - export PATH="/usr/lib/ccache:/usr/local/opt/ccache/libexec:$PATH" - CXXFLAGS="-O1 -g -w -DHAVE_BOOST" make -j$(nproc) testrunner HAVE_RULES=yes MATCHCOMPILER=yes - - - name: Run valgrind - run: | - ec=0 - valgrind --error-limit=yes --leak-check=full --num-callers=50 --show-reachable=yes --track-origins=yes --suppressions=valgrind/testrunner.supp --gen-suppressions=all --log-fd=9 --error-exitcode=42 ./testrunner TestGarbage TestOther TestSimplifyTemplate 9>memcheck.log || ec=1 - cat memcheck.log - exit $ec - env: - DEBUGINFOD_URLS: https://debuginfod.ubuntu.com - - - uses: actions/upload-artifact@v3 - if: success() || failure() - with: - name: Logs - path: ./*.log