Merge branch 'main' into chr_FixCrashLib

This commit is contained in:
chrchr 2022-07-08 18:12:21 +02:00
commit d264d3673f
77 changed files with 1353 additions and 2556 deletions

View File

@ -77,6 +77,8 @@ else # !WINNT
endif # !CPPCHK_GLIBCXX_DEBUG
endif # GNU/kFreeBSD
LDFLAGS=-pthread
endif # WINNT
ifdef CYGWIN
@ -207,8 +209,10 @@ EXTOBJ = externals/simplecpp/simplecpp.o \
CLIOBJ = cli/cmdlineparser.o \
cli/cppcheckexecutor.o \
cli/executor.o \
cli/filelister.o \
cli/main.o \
cli/processexecutor.o \
cli/threadexecutor.o
TESTOBJ = test/options.o \
@ -247,6 +251,7 @@ TESTOBJ = test/options.o \
test/testplatform.o \
test/testpostfixoperator.o \
test/testpreprocessor.o \
test/testprocessexecutor.o \
test/testrunner.o \
test/testsimplifytemplate.o \
test/testsimplifytokens.o \
@ -285,7 +290,7 @@ cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)
all: cppcheck testrunner
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)
test: all
@ -567,16 +572,22 @@ $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/ca
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp
cli/executor.o: cli/executor.cpp cli/executor.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/executor.o cli/executor.cpp
cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/filelister.o cli/filelister.cpp
cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/main.o cli/main.cpp
cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
cli/processexecutor.o: cli/processexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/processexecutor.o cli/processexecutor.cpp
cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp
test/options.o: test/options.cpp test/options.h
@ -687,6 +698,9 @@ test/testpostfixoperator.o: test/testpostfixoperator.cpp lib/check.h lib/checkpo
test/testpreprocessor.o: test/testpreprocessor.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testpreprocessor.o test/testpreprocessor.cpp
test/testprocessexecutor.o: test/testprocessexecutor.cpp cli/executor.h cli/processexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testprocessexecutor.o test/testprocessexecutor.cpp
test/testrunner.o: test/testrunner.cpp externals/simplecpp/simplecpp.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/preprocessor.h lib/suppressions.h test/options.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testrunner.o test/testrunner.cpp
@ -717,13 +731,13 @@ test/testsuite.o: test/testsuite.cpp lib/color.h lib/config.h lib/errorlogger.h
test/testsummaries.o: test/testsummaries.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/summaries.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsummaries.o test/testsummaries.cpp
test/testsuppressions.o: test/testsuppressions.cpp cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h
test/testsuppressions.o: test/testsuppressions.cpp cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsuppressions.o test/testsuppressions.cpp
test/testsymboldatabase.o: test/testsymboldatabase.cpp externals/tinyxml2/tinyxml2.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/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 test/testsuite.h test/testutils.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testsymboldatabase.o test/testsymboldatabase.cpp
test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/threadexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h
test/testthreadexecutor.o: test/testthreadexecutor.cpp cli/executor.h cli/threadexecutor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/timer.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/valueflow.h test/testsuite.h test/testutils.h
$(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o test/testthreadexecutor.o test/testthreadexecutor.cpp
test/testtimer.o: test/testtimer.cpp lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/timer.h test/testsuite.h

View File

@ -4063,8 +4063,9 @@ class MisraChecker:
errmsg = 'misra violation (use --rule-texts=<file> to get proper output)'
if self.path_premium_addon:
for line in cppcheckdata.cmd_output([self.path_premium_addon, '--cli', '--get-rule-text=' + errorId]).split('\n'):
if not line.startswith('{'):
errmsg = line
if len(line) > 1 and not line.startswith('{'):
errmsg = line.strip()
break
else:
errmsg = 'misra violation %s with no text in the supplied rule-texts-file' % (ruleNum)

View File

@ -36,11 +36,17 @@
<define name="G_STRINGIFY_ARG(contents)" value="#contents"/>
<define name="G_STRLOC" value="__FILE__ &quot;:&quot; G_STRINGIFY (__LINE__)"/>
<define name="G_STRFUNC" value="((const char*) (__FUNCTION__))"/>
<define name="G_DEFINE_TYPE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_TYPE_WITH_PRIVATE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_TYPE_WITH_CODE(TN, t_n, T_P, _C_)" value=""/>
<define name="G_DEFINE_FINAL_TYPE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_FINAL_TYPE_WITH_PRIVATE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_FINAL_TYPE_WITH_CODE(TN, t_n, T_P, _C_)" value=""/>
<define name="G_DEFINE_ABSTRACT_TYPE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_ABSTRACT_TYPE_WITH_CODE(TN, t_n, T_P, _C_)" value=""/>
<define name="G_DEFINE_DYNAMIC_TYPE(TN, t_n, T_P)" value=""/>
<define name="G_DEFINE_DYNAMIC_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_)" value=""/>
<define name="G_DEFINE_BOXED_TYPE(TypeName,type_name,copy_func,free_func)" value=""/>
<define name="G_DEFINE_BOXED_TYPE_WITH_CODE(TypeName,type_name,copy_func,free_func,_C_)" value=""/>
<define name="G_ADD_PRIVATE(TypeName)" value=""/>
@ -51,6 +57,9 @@
<define name="G_DEFINE_INTERFACE_WITH_CODE(TN, t_n, T_P, _C_)" value=""/>
<define name="G_DEFINE_TYPE_EXTENDED(TN, t_n, T_P, _f_, _C_)" value=""/>
<define name="G_IMPLEMENT_INTERFACE(TYPE_IFACE, iface_init)" value=""/>
<define name="G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(TypeName, func)" value=""/>
<define name="G_DEFINE_AUTO_CLEANUP_FREE_FUNC(TypeName, func, none)" value=""/>
<define name="G_DEFINE_AUTOPTR_CLEANUP_FUNC(TypeName, func)" value=""/>
<define name="MAX(a, b)" value="(((a) &gt; (b)) ? (a) : (b))"/>
<define name="MIN(a, b)" value="(((a) &lt; (b)) ? (a) : (b))"/>
<define name="ABS(a)" value="(((a) &lt; 0) ? -(a) : (a))"/>

View File

@ -13679,8 +13679,8 @@ wxItemKind kind = wxITEM_NORMAL) -->
<const/>
<use-retval/>
<leak-ignore/>
<arg nr="1" direction="in"/>
<arg nr="2" default="0" direction="in">
<arg nr="1" direction="out"/>
<arg nr="2" default="0" direction="out">
<not-uninit/>
<not-bool/>
</arg>

View File

@ -39,9 +39,7 @@ endif()
if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2)
target_link_libraries(cppcheck ${tinyxml2_LIBRARIES})
endif()
if (USE_THREADS)
target_link_libraries(cppcheck ${CMAKE_THREAD_LIBS_INIT})
endif()
target_link_libraries(cppcheck ${CMAKE_THREAD_LIBS_INIT})
add_dependencies(cppcheck copy_cfg)
add_dependencies(cppcheck copy_addons)

View File

@ -407,7 +407,9 @@
<ClInclude Include="..\lib\config.h" />
<ClInclude Include="cmdlineparser.h" />
<ClInclude Include="cppcheckexecutor.h" />
<ClInclude Include="executor.h" />
<ClInclude Include="filelister.h" />
<ClInclude Include="processexecutor.h" />
<ClInclude Include="threadexecutor.h" />
</ItemGroup>
<ItemGroup>
@ -418,8 +420,10 @@
<ItemGroup>
<ClCompile Include="cmdlineparser.cpp" />
<ClCompile Include="cppcheckexecutor.cpp" />
<ClCompile Include="executor.cpp" />
<ClCompile Include="filelister.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="processexecutor.cpp" />
<ClCompile Include="threadexecutor.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />

View File

@ -36,6 +36,12 @@
#include "utils.h"
#include "checkunusedfunctions.h"
#if defined(THREADING_MODEL_THREAD)
#include "threadexecutor.h"
#elif defined(THREADING_MODEL_FORK)
#include "processexecutor.h"
#endif
#include <algorithm>
#include <atomic>
#include <csignal>
@ -122,7 +128,7 @@ bool CppCheckExecutor::parseFromArgs(CppCheck *cppcheck, int argc, const char* c
if (parser.getShowErrorMessages()) {
mShowAllErrors = true;
std::cout << ErrorMessage::getXMLHeader();
std::cout << ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName);
cppcheck->getErrorMessages();
std::cout << ErrorMessage::getXMLFooter() << std::endl;
}
@ -908,7 +914,7 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
}
if (settings.xml) {
reportErr(ErrorMessage::getXMLHeader());
reportErr(ErrorMessage::getXMLHeader(settings.cppcheckCfgProductName));
}
if (!settings.buildDir.empty()) {
@ -972,8 +978,11 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
} else if (!ThreadExecutor::isEnabled()) {
std::cout << "No thread support yet implemented for this platform." << std::endl;
} else {
// Multiple processes
#if defined(THREADING_MODEL_THREAD)
ThreadExecutor executor(mFiles, settings, *this);
#elif defined(THREADING_MODEL_FORK)
ProcessExecutor executor(mFiles, settings, *this);
#endif
returnValue = executor.check();
}

30
cli/executor.cpp Normal file
View File

@ -0,0 +1,30 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 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 <http://www.gnu.org/licenses/>.
*/
#include "executor.h"
Executor::Executor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
: mFiles(files), mSettings(settings), mErrorLogger(errorLogger)
{}
Executor::~Executor()
{}
bool Executor::isEnabled() {
return true;
}

60
cli/executor.h Normal file
View File

@ -0,0 +1,60 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 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 <http://www.gnu.org/licenses/>.
*/
#ifndef EXECUTOR_H
#define EXECUTOR_H
#include <cstddef>
#include <list>
#include <map>
#include <string>
class Settings;
class ErrorLogger;
/// @addtogroup CLI
/// @{
/**
* This class will take a list of filenames and settings and check then
* all files using threads.
*/
class Executor {
public:
Executor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger);
Executor(const Executor &) = delete;
virtual ~Executor();
void operator=(const Executor &) = delete;
virtual unsigned int check() = 0;
/**
* @return true if support for threads exist.
*/
static bool isEnabled();
protected:
const std::map<std::string, std::size_t> &mFiles;
Settings &mSettings;
ErrorLogger &mErrorLogger;
std::list<std::string> mErrorList;
};
/// @}
#endif // EXECUTOR_H

383
cli/processexecutor.cpp Normal file
View File

@ -0,0 +1,383 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 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 <http://www.gnu.org/licenses/>.
*/
#include "processexecutor.h"
#if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
#include "color.h"
#include "config.h"
#include "cppcheck.h"
#include "cppcheckexecutor.h"
#include "errorlogger.h"
#include "errortypes.h"
#include "importproject.h"
#include "settings.h"
#include "suppressions.h"
#include <algorithm>
#include <cerrno>
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <functional>
#include <iostream>
#include <list>
#include <sys/select.h>
#include <sys/wait.h>
#include <unistd.h>
#include <utility>
#ifdef __SVR4 // Solaris
#include <sys/loadavg.h>
#endif
#if defined(__linux__)
#include <sys/prctl.h>
#endif
// NOLINTNEXTLINE(misc-unused-using-decls) - required for FD_ZERO
using std::memset;
ProcessExecutor::ProcessExecutor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
: Executor(files, settings, errorLogger)
{}
ProcessExecutor::~ProcessExecutor()
{}
class PipeWriter : public ErrorLogger {
public:
enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', REPORT_VERIFICATION='4', CHILD_END='5'};
explicit PipeWriter(int pipe) : mWpipe(pipe) {}
void reportOut(const std::string &outmsg, Color c) override {
writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset));
}
void reportErr(const ErrorMessage &msg) override {
report(msg, MessageType::REPORT_ERROR);
}
void reportInfo(const ErrorMessage &msg) override {
report(msg, MessageType::REPORT_INFO);
}
void writeEnd(const std::string& str) {
writeToPipe(CHILD_END, str);
}
private:
enum class MessageType {REPORT_ERROR, REPORT_INFO};
void report(const ErrorMessage &msg, MessageType msgType) {
PipeSignal pipeSignal;
switch (msgType) {
case MessageType::REPORT_ERROR:
pipeSignal = REPORT_ERROR;
break;
case MessageType::REPORT_INFO:
pipeSignal = REPORT_INFO;
break;
}
writeToPipe(pipeSignal, msg.serialize());
}
void writeToPipe(PipeSignal type, const std::string &data)
{
unsigned int len = static_cast<unsigned int>(data.length() + 1);
char *out = new char[len + 1 + sizeof(len)];
out[0] = static_cast<char>(type);
std::memcpy(&(out[1]), &len, sizeof(len));
std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) {
delete[] out;
out = nullptr;
std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl;
std::exit(EXIT_FAILURE);
}
delete[] out;
}
const int mWpipe;
};
int ProcessExecutor::handleRead(int rpipe, unsigned int &result)
{
char type = 0;
if (read(rpipe, &type, 1) <= 0) {
if (errno == EAGAIN)
return 0;
// need to increment so a missing pipe (i.e. premature exit of forked process) results in an error exitcode
++result;
return -1;
}
if (type != PipeWriter::REPORT_OUT && type != PipeWriter::REPORT_ERROR && type != PipeWriter::REPORT_INFO && type != PipeWriter::CHILD_END) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
unsigned int len = 0;
if (read(rpipe, &len, sizeof(len)) <= 0) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
// Don't rely on incoming data being null-terminated.
// Allocate +1 element and null-terminate the buffer.
char *buf = new char[len + 1];
const ssize_t readIntoBuf = read(rpipe, buf, len);
if (readIntoBuf <= 0) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
buf[readIntoBuf] = 0;
if (type == PipeWriter::REPORT_OUT) {
mErrorLogger.reportOut(buf);
} else if (type == PipeWriter::REPORT_ERROR || type == PipeWriter::REPORT_INFO) {
ErrorMessage msg;
try {
msg.deserialize(buf);
} catch (const InternalError& e) {
std::cerr << "#### ThreadExecutor::handleRead error, internal error:" << e.errorMessage << std::endl;
std::exit(EXIT_FAILURE);
}
if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) {
// Alert only about unique errors
std::string errmsg = msg.toString(mSettings.verbose);
if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) {
mErrorList.emplace_back(errmsg);
if (type == PipeWriter::REPORT_ERROR)
mErrorLogger.reportErr(msg);
else
mErrorLogger.reportInfo(msg);
}
}
} else if (type == PipeWriter::CHILD_END) {
std::istringstream iss(buf);
unsigned int fileResult = 0;
iss >> fileResult;
result += fileResult;
delete[] buf;
return -1;
}
delete[] buf;
return 1;
}
bool ProcessExecutor::checkLoadAverage(size_t nchildren)
{
#if defined(__CYGWIN__) || defined(__QNX__) || defined(__HAIKU__) // getloadavg() is unsupported on Cygwin, Qnx, Haiku.
(void)nchildren;
return true;
#else
if (!nchildren || !mSettings.loadAverage) {
return true;
}
double sample(0);
if (getloadavg(&sample, 1) != 1) {
// disable load average checking on getloadavg error
return true;
} else if (sample < mSettings.loadAverage) {
return true;
}
return false;
#endif
}
unsigned int ProcessExecutor::check()
{
unsigned int fileCount = 0;
unsigned int result = 0;
std::size_t totalfilesize = 0;
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
totalfilesize += i->second;
}
std::list<int> rpipes;
std::map<pid_t, std::string> childFile;
std::map<int, std::string> pipeFile;
std::size_t processedsize = 0;
std::map<std::string, std::size_t>::const_iterator iFile = mFiles.begin();
std::list<ImportProject::FileSettings>::const_iterator iFileSettings = mSettings.project.fileSettings.begin();
for (;;) {
// Start a new child
size_t nchildren = childFile.size();
if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) {
int pipes[2];
if (pipe(pipes) == -1) {
std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
int flags = 0;
if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) {
std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) {
std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid < 0) {
// Error
std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
} else if (pid == 0) {
#if defined(__linux__)
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
close(pipes[0]);
PipeWriter pipewriter(pipes[1]);
CppCheck fileChecker(pipewriter, false, CppCheckExecutor::executeCommand);
fileChecker.settings() = mSettings;
unsigned int resultOfCheck = 0;
if (iFileSettings != mSettings.project.fileSettings.end()) {
resultOfCheck = fileChecker.check(*iFileSettings);
} else {
// Read file from a file
resultOfCheck = fileChecker.check(iFile->first);
}
std::ostringstream oss;
oss << resultOfCheck;
pipewriter.writeEnd(oss.str());
std::exit(EXIT_SUCCESS);
}
close(pipes[1]);
rpipes.push_back(pipes[0]);
if (iFileSettings != mSettings.project.fileSettings.end()) {
childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg;
pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg;
++iFileSettings;
} else {
childFile[pid] = iFile->first;
pipeFile[pipes[0]] = iFile->first;
++iFile;
}
}
if (!rpipes.empty()) {
fd_set rfds;
FD_ZERO(&rfds);
for (std::list<int>::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp)
FD_SET(*rp, &rfds);
struct timeval tv; // for every second polling of load average condition
tv.tv_sec = 1;
tv.tv_usec = 0;
int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv);
if (r > 0) {
std::list<int>::iterator rp = rpipes.begin();
while (rp != rpipes.end()) {
if (FD_ISSET(*rp, &rfds)) {
int readRes = handleRead(*rp, result);
if (readRes == -1) {
std::size_t size = 0;
std::map<int, std::string>::iterator p = pipeFile.find(*rp);
if (p != pipeFile.end()) {
std::string name = p->second;
pipeFile.erase(p);
std::map<std::string, std::size_t>::const_iterator fs = mFiles.find(name);
if (fs != mFiles.end()) {
size = fs->second;
}
}
fileCount++;
processedsize += size;
if (!mSettings.quiet)
CppCheckExecutor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize);
close(*rp);
rp = rpipes.erase(rp);
} else
++rp;
} else
++rp;
}
}
}
if (!childFile.empty()) {
int stat = 0;
pid_t child = waitpid(0, &stat, WNOHANG);
if (child > 0) {
std::string childname;
std::map<pid_t, std::string>::iterator c = childFile.find(child);
if (c != childFile.end()) {
childname = c->second;
childFile.erase(c);
}
if (WIFEXITED(stat)) {
const int exitstatus = WEXITSTATUS(stat);
if (exitstatus != EXIT_SUCCESS) {
std::ostringstream oss;
oss << "Child process exited with " << exitstatus;
reportInternalChildErr(childname, oss.str());
}
} else if (WIFSIGNALED(stat)) {
std::ostringstream oss;
oss << "Child process crashed with signal " << WTERMSIG(stat);
reportInternalChildErr(childname, oss.str());
}
}
}
if (iFile == mFiles.end() && iFileSettings == mSettings.project.fileSettings.end() && rpipes.empty() && childFile.empty()) {
// All done
break;
}
}
return result;
}
void ProcessExecutor::reportInternalChildErr(const std::string &childname, const std::string &msg)
{
std::list<ErrorMessage::FileLocation> locations;
locations.emplace_back(childname, 0, 0);
const ErrorMessage errmsg(locations,
emptyString,
Severity::error,
"Internal error: " + msg,
"cppcheckError",
Certainty::normal);
if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage()))
mErrorLogger.reportErr(errmsg);
}
#endif // !WIN32

72
cli/processexecutor.h Normal file
View File

@ -0,0 +1,72 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 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 <http://www.gnu.org/licenses/>.
*/
#ifndef PROCESSEXECUTOR_H
#define PROCESSEXECUTOR_H
#include "executor.h"
#include <cstddef>
#include <map>
#include <string>
class Settings;
class ErrorLogger;
/// @addtogroup CLI
/// @{
/**
* This class will take a list of filenames and settings and check then
* all files using threads.
*/
class ProcessExecutor : public Executor {
public:
ProcessExecutor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger);
ProcessExecutor(const ProcessExecutor &) = delete;
~ProcessExecutor();
void operator=(const ProcessExecutor &) = delete;
unsigned int check() override;
private:
/**
* Read from the pipe, parse and handle what ever is in there.
*@return -1 in case of error
* 0 if there is nothing in the pipe to be read
* 1 if we did read something
*/
int handleRead(int rpipe, unsigned int &result);
/**
* @brief Check load average condition
* @param nchildren - count of currently ran children
* @return true - if new process can be started
*/
bool checkLoadAverage(size_t nchildren);
/**
* @brief Reports internal errors related to child processes
* @param msg The error message
*/
void reportInternalChildErr(const std::string &childname, const std::string &msg);
};
/// @}
#endif // PROCESSEXECUTOR_H

View File

@ -29,379 +29,33 @@
#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <utility>
#ifdef __SVR4 // Solaris
#include <sys/loadavg.h>
#endif
#ifdef THREADING_MODEL_FORK
#include "config.h"
#include "errortypes.h"
#if defined(__linux__)
#include <sys/prctl.h>
#endif
#include <cerrno>
#include <cstring>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <csignal>
#include <unistd.h>
// NOLINTNEXTLINE(misc-unused-using-decls) - required for FD_ZERO
using std::memset;
#endif
#ifdef THREADING_MODEL_THREAD
#include <future>
#include <iostream>
#include <list>
#include <numeric>
#endif
#include <mutex>
#include <system_error>
#include <utility>
#include <vector>
ThreadExecutor::ThreadExecutor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
: mFiles(files), mSettings(settings), mErrorLogger(errorLogger)
: Executor(files, settings, errorLogger)
{}
ThreadExecutor::~ThreadExecutor()
{}
///////////////////////////////////////////////////////////////////////////////
////// This code is for platforms that support fork() only ////////////////////
///////////////////////////////////////////////////////////////////////////////
#if defined(THREADING_MODEL_FORK)
class PipeWriter : public ErrorLogger {
public:
enum PipeSignal {REPORT_OUT='1',REPORT_ERROR='2', REPORT_INFO='3', REPORT_VERIFICATION='4', CHILD_END='5'};
explicit PipeWriter(int pipe) : mWpipe(pipe) {}
void reportOut(const std::string &outmsg, Color c) override {
writeToPipe(REPORT_OUT, ::toString(c) + outmsg + ::toString(Color::Reset));
}
void reportErr(const ErrorMessage &msg) override {
report(msg, MessageType::REPORT_ERROR);
}
void reportInfo(const ErrorMessage &msg) override {
report(msg, MessageType::REPORT_INFO);
}
void writeEnd(const std::string& str) {
writeToPipe(CHILD_END, str);
}
private:
enum class MessageType {REPORT_ERROR, REPORT_INFO};
void report(const ErrorMessage &msg, MessageType msgType) {
PipeSignal pipeSignal;
switch (msgType) {
case MessageType::REPORT_ERROR:
pipeSignal = REPORT_ERROR;
break;
case MessageType::REPORT_INFO:
pipeSignal = REPORT_INFO;
break;
}
writeToPipe(pipeSignal, msg.serialize());
}
void writeToPipe(PipeSignal type, const std::string &data)
{
unsigned int len = static_cast<unsigned int>(data.length() + 1);
char *out = new char[len + 1 + sizeof(len)];
out[0] = static_cast<char>(type);
std::memcpy(&(out[1]), &len, sizeof(len));
std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
if (write(mWpipe, out, len + 1 + sizeof(len)) <= 0) {
delete[] out;
out = nullptr;
std::cerr << "#### ThreadExecutor::writeToPipe, Failed to write to pipe" << std::endl;
std::exit(EXIT_FAILURE);
}
delete[] out;
}
const int mWpipe;
};
int ThreadExecutor::handleRead(int rpipe, unsigned int &result)
{
char type = 0;
if (read(rpipe, &type, 1) <= 0) {
if (errno == EAGAIN)
return 0;
// need to increment so a missing pipe (i.e. premature exit of forked process) results in an error exitcode
++result;
return -1;
}
if (type != PipeWriter::REPORT_OUT && type != PipeWriter::REPORT_ERROR && type != PipeWriter::REPORT_INFO && type != PipeWriter::CHILD_END) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
unsigned int len = 0;
if (read(rpipe, &len, sizeof(len)) <= 0) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
// Don't rely on incoming data being null-terminated.
// Allocate +1 element and null-terminate the buffer.
char *buf = new char[len + 1];
const ssize_t readIntoBuf = read(rpipe, buf, len);
if (readIntoBuf <= 0) {
std::cerr << "#### ThreadExecutor::handleRead error, type was:" << type << std::endl;
std::exit(EXIT_FAILURE);
}
buf[readIntoBuf] = 0;
if (type == PipeWriter::REPORT_OUT) {
mErrorLogger.reportOut(buf);
} else if (type == PipeWriter::REPORT_ERROR || type == PipeWriter::REPORT_INFO) {
ErrorMessage msg;
try {
msg.deserialize(buf);
} catch (const InternalError& e) {
std::cerr << "#### ThreadExecutor::handleRead error, internal error:" << e.errorMessage << std::endl;
std::exit(EXIT_FAILURE);
}
if (!mSettings.nomsg.isSuppressed(msg.toSuppressionsErrorMessage())) {
// Alert only about unique errors
std::string errmsg = msg.toString(mSettings.verbose);
if (std::find(mErrorList.begin(), mErrorList.end(), errmsg) == mErrorList.end()) {
mErrorList.emplace_back(errmsg);
if (type == PipeWriter::REPORT_ERROR)
mErrorLogger.reportErr(msg);
else
mErrorLogger.reportInfo(msg);
}
}
} else if (type == PipeWriter::CHILD_END) {
std::istringstream iss(buf);
unsigned int fileResult = 0;
iss >> fileResult;
result += fileResult;
delete[] buf;
return -1;
}
delete[] buf;
return 1;
}
bool ThreadExecutor::checkLoadAverage(size_t nchildren)
{
#if defined(__CYGWIN__) || defined(__QNX__) || defined(__HAIKU__) // getloadavg() is unsupported on Cygwin, Qnx, Haiku.
return true;
#else
if (!nchildren || !mSettings.loadAverage) {
return true;
}
double sample(0);
if (getloadavg(&sample, 1) != 1) {
// disable load average checking on getloadavg error
return true;
} else if (sample < mSettings.loadAverage) {
return true;
}
return false;
#endif
}
unsigned int ThreadExecutor::check()
{
unsigned int fileCount = 0;
unsigned int result = 0;
std::size_t totalfilesize = 0;
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.begin(); i != mFiles.end(); ++i) {
totalfilesize += i->second;
}
std::list<int> rpipes;
std::map<pid_t, std::string> childFile;
std::map<int, std::string> pipeFile;
std::size_t processedsize = 0;
std::map<std::string, std::size_t>::const_iterator iFile = mFiles.begin();
std::list<ImportProject::FileSettings>::const_iterator iFileSettings = mSettings.project.fileSettings.begin();
for (;;) {
// Start a new child
size_t nchildren = childFile.size();
if ((iFile != mFiles.end() || iFileSettings != mSettings.project.fileSettings.end()) && nchildren < mSettings.jobs && checkLoadAverage(nchildren)) {
int pipes[2];
if (pipe(pipes) == -1) {
std::cerr << "#### ThreadExecutor::check, pipe() failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
int flags = 0;
if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0) {
std::cerr << "#### ThreadExecutor::check, fcntl(F_GETFL) failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0) {
std::cerr << "#### ThreadExecutor::check, fcntl(F_SETFL) failed: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
}
pid_t pid = fork();
if (pid < 0) {
// Error
std::cerr << "#### ThreadExecutor::check, Failed to create child process: "<< std::strerror(errno) << std::endl;
std::exit(EXIT_FAILURE);
} else if (pid == 0) {
#if defined(__linux__)
prctl(PR_SET_PDEATHSIG, SIGHUP);
#endif
close(pipes[0]);
PipeWriter pipewriter(pipes[1]);
CppCheck fileChecker(pipewriter, false, CppCheckExecutor::executeCommand);
fileChecker.settings() = mSettings;
unsigned int resultOfCheck = 0;
if (iFileSettings != mSettings.project.fileSettings.end()) {
resultOfCheck = fileChecker.check(*iFileSettings);
} else {
// Read file from a file
resultOfCheck = fileChecker.check(iFile->first);
}
std::ostringstream oss;
oss << resultOfCheck;
pipewriter.writeEnd(oss.str());
std::exit(EXIT_SUCCESS);
}
close(pipes[1]);
rpipes.push_back(pipes[0]);
if (iFileSettings != mSettings.project.fileSettings.end()) {
childFile[pid] = iFileSettings->filename + ' ' + iFileSettings->cfg;
pipeFile[pipes[0]] = iFileSettings->filename + ' ' + iFileSettings->cfg;
++iFileSettings;
} else {
childFile[pid] = iFile->first;
pipeFile[pipes[0]] = iFile->first;
++iFile;
}
}
if (!rpipes.empty()) {
fd_set rfds;
FD_ZERO(&rfds);
for (std::list<int>::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp)
FD_SET(*rp, &rfds);
struct timeval tv; // for every second polling of load average condition
tv.tv_sec = 1;
tv.tv_usec = 0;
int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, nullptr, nullptr, &tv);
if (r > 0) {
std::list<int>::iterator rp = rpipes.begin();
while (rp != rpipes.end()) {
if (FD_ISSET(*rp, &rfds)) {
int readRes = handleRead(*rp, result);
if (readRes == -1) {
std::size_t size = 0;
std::map<int, std::string>::iterator p = pipeFile.find(*rp);
if (p != pipeFile.end()) {
std::string name = p->second;
pipeFile.erase(p);
std::map<std::string, std::size_t>::const_iterator fs = mFiles.find(name);
if (fs != mFiles.end()) {
size = fs->second;
}
}
fileCount++;
processedsize += size;
if (!mSettings.quiet)
CppCheckExecutor::reportStatus(fileCount, mFiles.size() + mSettings.project.fileSettings.size(), processedsize, totalfilesize);
close(*rp);
rp = rpipes.erase(rp);
} else
++rp;
} else
++rp;
}
}
}
if (!childFile.empty()) {
int stat = 0;
pid_t child = waitpid(0, &stat, WNOHANG);
if (child > 0) {
std::string childname;
std::map<pid_t, std::string>::iterator c = childFile.find(child);
if (c != childFile.end()) {
childname = c->second;
childFile.erase(c);
}
if (WIFEXITED(stat)) {
const int exitstatus = WEXITSTATUS(stat);
if (exitstatus != EXIT_SUCCESS) {
std::ostringstream oss;
oss << "Child process exited with " << exitstatus;
reportInternalChildErr(childname, oss.str());
}
} else if (WIFSIGNALED(stat)) {
std::ostringstream oss;
oss << "Child process crashed with signal " << WTERMSIG(stat);
reportInternalChildErr(childname, oss.str());
}
}
}
if (iFile == mFiles.end() && iFileSettings == mSettings.project.fileSettings.end() && rpipes.empty() && childFile.empty()) {
// All done
break;
}
}
return result;
}
void ThreadExecutor::reportInternalChildErr(const std::string &childname, const std::string &msg)
{
std::list<ErrorMessage::FileLocation> locations;
locations.emplace_back(childname, 0, 0);
const ErrorMessage errmsg(locations,
emptyString,
Severity::error,
"Internal error: " + msg,
"cppcheckError",
Certainty::normal);
if (!mSettings.nomsg.isSuppressed(errmsg.toSuppressionsErrorMessage()))
mErrorLogger.reportErr(errmsg);
}
#elif defined(THREADING_MODEL_THREAD)
class ThreadExecutor::SyncLogForwarder : public ErrorLogger
{
public:
SyncLogForwarder(ThreadExecutor &threadExecutor)
explicit SyncLogForwarder(ThreadExecutor &threadExecutor)
: mThreadExecutor(threadExecutor), mProcessedFiles(0), mTotalFiles(0), mProcessedSize(0), mTotalFileSize(0) {
mItNextFile = threadExecutor.mFiles.begin();
mItNextFileSettings = threadExecutor.mSettings.project.fileSettings.begin();
mItNextFile = mThreadExecutor.mFiles.begin();
mItNextFileSettings = mThreadExecutor.mSettings.project.fileSettings.begin();
mTotalFiles = threadExecutor.mFiles.size() + threadExecutor.mSettings.project.fileSettings.size();
for (std::map<std::string, std::size_t>::const_iterator i = threadExecutor.mFiles.begin(); i != threadExecutor.mFiles.end(); ++i) {
mTotalFiles = mThreadExecutor.mFiles.size() + mThreadExecutor.mSettings.project.fileSettings.size();
for (std::map<std::string, std::size_t>::const_iterator i = mThreadExecutor.mFiles.begin(); i != mThreadExecutor.mFiles.end(); ++i) {
mTotalFileSize += i->second;
}
}
@ -541,8 +195,3 @@ unsigned int STDCALL ThreadExecutor::threadProc(SyncLogForwarder* logForwarder)
}
return result;
}
#endif
bool ThreadExecutor::isEnabled() {
return true;
}

View File

@ -21,8 +21,9 @@
#include "config.h"
#include "executor.h"
#include <cstddef>
#include <list>
#include <map>
#include <string>
@ -36,55 +37,18 @@ class ErrorLogger;
* This class will take a list of filenames and settings and check then
* all files using threads.
*/
class ThreadExecutor {
class ThreadExecutor : public Executor {
public:
ThreadExecutor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger);
ThreadExecutor(const ThreadExecutor &) = delete;
~ThreadExecutor();
void operator=(const ThreadExecutor &) = delete;
unsigned int check();
unsigned int check() override;
private:
const std::map<std::string, std::size_t> &mFiles;
Settings &mSettings;
ErrorLogger &mErrorLogger;
std::list<std::string> mErrorList;
#if defined(THREADING_MODEL_FORK)
/**
* Read from the pipe, parse and handle what ever is in there.
*@return -1 in case of error
* 0 if there is nothing in the pipe to be read
* 1 if we did read something
*/
int handleRead(int rpipe, unsigned int &result);
/**
* @brief Check load average condition
* @param nchildren - count of currently ran children
* @return true - if new process can be started
*/
bool checkLoadAverage(size_t nchildren);
/**
* @brief Reports internal errors related to child processes
* @param msg The error message
*/
void reportInternalChildErr(const std::string &childname, const std::string &msg);
#elif defined(THREADING_MODEL_THREAD)
class SyncLogForwarder;
static unsigned int STDCALL threadProc(SyncLogForwarder *logforwarder);
#endif
public:
/**
* @return true if support for threads exist.
*/
static bool isEnabled();
static unsigned int STDCALL threadProc(SyncLogForwarder *logForwarder);
};
/// @}

View File

@ -68,9 +68,7 @@ if (NOT USE_BUNDLED_TINYXML2)
endif()
endif()
if (USE_THREADS)
find_package(Threads REQUIRED)
endif()
find_package(Threads REQUIRED)
if (USE_BOOST)
find_package(Boost COMPONENTS container QUIET)

View File

@ -52,9 +52,7 @@ if (HAVE_RULES)
endif()
message( STATUS )
message( STATUS "USE_THREADS = ${USE_THREADS}" )
if (USE_THREADS)
message( STATUS "CMAKE_THREAD_LIBS_INIT = ${CMAKE_THREAD_LIBS_INIT}" )
endif()
message( STATUS "CMAKE_THREAD_LIBS_INIT = ${CMAKE_THREAD_LIBS_INIT}" )
if (NOT USE_MATCHCOMPILER_OPT MATCHES "Off")
message( STATUS )
message( STATUS "PYTHON_VERSION_STRING = ${PYTHON_VERSION_STRING}" )

View File

@ -45,26 +45,26 @@ Highlighter::Highlighter(QTextDocument *parent,
<< "case"
<< "catch"
<< "char"
<< "char8_­t"
<< "char16_­t"
<< "char32_­t"
<< "char8_t"
<< "char16_t"
<< "char32_t"
<< "class"
<< "concept"
<< "const"
<< "consteval"
<< "constexpr"
<< "constinit"
<< "const_­cast"
<< "const_cast"
<< "continue"
<< "co_­await"
<< "co_­return"
<< "co_­yield"
<< "co_await"
<< "co_return"
<< "co_yield"
<< "decltype"
<< "default"
<< "delete"
<< "do"
<< "double"
<< "dynamic_­cast"
<< "dynamic_cast"
<< "else"
<< "enum"
<< "explicit"
@ -92,19 +92,19 @@ Highlighter::Highlighter(QTextDocument *parent,
<< "private"
<< "protected"
<< "public"
<< "reinterpret_­cast"
<< "reinterpret_cast"
<< "requires"
<< "return"
<< "short"
<< "signed"
<< "static"
<< "static_­assert"
<< "static_­cast"
<< "static_assert"
<< "static_cast"
<< "struct"
<< "switch"
<< "template"
<< "this"
<< "thread_­local"
<< "thread_local"
<< "throw"
<< "true"
<< "try"
@ -116,7 +116,7 @@ Highlighter::Highlighter(QTextDocument *parent,
<< "virtual"
<< "void"
<< "volatile"
<< "wchar_­t"
<< "wchar_t"
<< "while";
for (const QString &pattern : keywordPatterns) {
rule.pattern = QRegularExpression("\\b" + pattern + "\\b");
@ -157,7 +157,9 @@ Highlighter::Highlighter(QTextDocument *parent,
mSymbolFormat.setBackground(mWidgetStyle->symbolBGColor);
mSymbolFormat.setFontWeight(mWidgetStyle->symbolWeight);
mCommentStartExpression = QRegularExpression("/\\*");
// We use negative lookbehind assertion `(?<!/)`
// to ignore case: single line comment and line of asterisk
mCommentStartExpression = QRegularExpression("(?<!/)/\\*");
mCommentEndExpression = QRegularExpression("\\*/");
}

View File

@ -61,7 +61,7 @@ static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader)
if (elementName == "type") {
container.type.templateParameter = xmlReader.attributes().value("templateParameter").toString();
container.type.string = xmlReader.attributes().value("string").toString();
} else if (elementName == "size" || elementName == "access" || elementName == "other") {
} else if (elementName == "size" || elementName == "access" || elementName == "other" || elementName == "rangeItemRecordType") {
const QString indexOperator = xmlReader.attributes().value("indexOperator").toString();
if (elementName == "access" && indexOperator == "array-like")
container.access_arrayLike = true;
@ -82,7 +82,12 @@ static CppcheckLibraryData::Container loadContainer(QXmlStreamReader &xmlReader)
container.sizeFunctions.append(function);
else if (elementName == "access")
container.accessFunctions.append(function);
else
else if (elementName == "rangeItemRecordType") {
struct CppcheckLibraryData::Container::RangeItemRecordType rangeItemRecordType;
rangeItemRecordType.name = xmlReader.attributes().value("name").toString();
rangeItemRecordType.templateParameter = xmlReader.attributes().value("templateParameter").toString();
container.rangeItemRecordTypeList.append(rangeItemRecordType);
} else
container.otherFunctions.append(function);
}
} else {
@ -105,9 +110,23 @@ static QString loadUndefine(const QXmlStreamReader &xmlReader)
return xmlReader.attributes().value("name").toString();
}
static QString loadSmartPointer(const QXmlStreamReader &xmlReader)
static CppcheckLibraryData::SmartPointer loadSmartPointer(QXmlStreamReader &xmlReader)
{
return xmlReader.attributes().value("class-name").toString();
CppcheckLibraryData::SmartPointer smartPointer;
smartPointer.name = xmlReader.attributes().value("class-name").toString();
QXmlStreamReader::TokenType type;
while ((type = xmlReader.readNext()) != QXmlStreamReader::EndElement ||
xmlReader.name().toString() != "smart-pointer") {
if (type != QXmlStreamReader::StartElement)
continue;
const QString elementName = xmlReader.name().toString();
if (elementName == "unique") {
smartPointer.unique = true;
} else {
unhandledElement(xmlReader);
}
}
return smartPointer;
}
static CppcheckLibraryData::TypeChecks loadTypeChecks(QXmlStreamReader &xmlReader)
@ -210,6 +229,20 @@ static CppcheckLibraryData::Function loadFunction(QXmlStreamReader &xmlReader, c
function.warn.reason = xmlReader.attributes().value("reason").toString();
function.warn.alternatives = xmlReader.attributes().value("alternatives").toString();
function.warn.msg = xmlReader.readElementText();
} else if (elementName == "not-overlapping-data") {
const QStringList attributeList {"ptr1-arg", "ptr2-arg", "size-arg", "strlen-arg"};
for (const QString &attr : attributeList) {
if (xmlReader.attributes().hasAttribute(attr)) {
function.notOverlappingDataArgs[attr] = xmlReader.attributes().value(attr).toString();
}
}
} else if (elementName == "container") {
const QStringList attributeList {"action", "yields"};
for (const QString &attr : attributeList) {
if (xmlReader.attributes().hasAttribute(attr)) {
function.containerAttributes[attr] = xmlReader.attributes().value(attr).toString();
}
}
} else {
unhandledElement(xmlReader);
}
@ -493,6 +526,20 @@ static void writeContainerFunctions(QXmlStreamWriter &xmlWriter, const QString &
xmlWriter.writeEndElement();
}
static void writeContainerRangeItemRecords(QXmlStreamWriter &xmlWriter, const QList<struct CppcheckLibraryData::Container::RangeItemRecordType> &rangeItemRecords)
{
if (rangeItemRecords.isEmpty())
return;
xmlWriter.writeStartElement("rangeItemRecordType");
for (const CppcheckLibraryData::Container::RangeItemRecordType &item : rangeItemRecords) {
xmlWriter.writeStartElement("member");
xmlWriter.writeAttribute("name", item.name);
xmlWriter.writeAttribute("templateParameter", item.templateParameter);
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData::Container &container)
{
xmlWriter.writeStartElement("container");
@ -519,6 +566,7 @@ static void writeContainer(QXmlStreamWriter &xmlWriter, const CppcheckLibraryDat
writeContainerFunctions(xmlWriter, "size", container.size_templateParameter, container.sizeFunctions);
writeContainerFunctions(xmlWriter, "access", container.access_arrayLike?1:-1, container.accessFunctions);
writeContainerFunctions(xmlWriter, "other", -1, container.otherFunctions);
writeContainerRangeItemRecords(xmlWriter, container.rangeItemRecordTypeList);
xmlWriter.writeEndElement();
}
@ -632,7 +680,20 @@ static void writeFunction(QXmlStreamWriter &xmlWriter, const CppcheckLibraryData
xmlWriter.writeEndElement();
}
if (!function.notOverlappingDataArgs.isEmpty()) {
xmlWriter.writeStartElement("not-overlapping-data");
foreach (const QString value, function.notOverlappingDataArgs) {
xmlWriter.writeAttribute(function.notOverlappingDataArgs.key(value), value);
}
xmlWriter.writeEndElement();
}
if (!function.containerAttributes.isEmpty()) {
xmlWriter.writeStartElement("container");
foreach (const QString value, function.containerAttributes) {
xmlWriter.writeAttribute(function.containerAttributes.key(value), value);
}
xmlWriter.writeEndElement();
}
xmlWriter.writeEndElement();
}
@ -835,9 +896,12 @@ QString CppcheckLibraryData::toString() const
writeTypeChecks(xmlWriter, check);
}
for (const QString &smartPtr : smartPointers) {
for (const SmartPointer &smartPtr : smartPointers) {
xmlWriter.writeStartElement("smart-pointer");
xmlWriter.writeAttribute("class-name", smartPtr);
xmlWriter.writeAttribute("class-name", smartPtr.name);
if (smartPtr.unique) {
xmlWriter.writeEmptyElement("unique");
}
xmlWriter.writeEndElement();
}

View File

@ -22,6 +22,7 @@
#include <QList>
#include <QString>
#include <QStringList>
#include <QMap>
class QIODevice;
@ -47,6 +48,11 @@ public:
QString string;
} type;
struct RangeItemRecordType {
QString name;
QString templateParameter;
};
struct Function {
QString name;
QString yields;
@ -55,6 +61,7 @@ public:
QList<struct Function> accessFunctions;
QList<struct Function> otherFunctions;
QList<struct Function> sizeFunctions;
QList<struct RangeItemRecordType> rangeItemRecordTypeList;
};
struct Define {
@ -130,6 +137,9 @@ public:
msg.isEmpty();
}
} warn;
QMap<QString, QString> notOverlappingDataArgs;
QMap<QString, QString> containerAttributes;
};
struct MemoryResource {
@ -219,6 +229,15 @@ public:
QList<Exporter> exporter;
};
struct SmartPointer {
SmartPointer() :
unique {false}
{}
QString name;
bool unique;
};
void clear() {
containers.clear();
defines.clear();
@ -258,7 +277,7 @@ public:
QList<TypeChecks> typeChecks;
QList<struct PlatformType> platformTypes;
QStringList undefines;
QStringList smartPointers;
QList<struct SmartPointer> smartPointers;
QList<struct Reflection> reflections;
QList<struct Markup> markups;
};

View File

@ -0,0 +1,9 @@
<?xml version="1.0"?>
<def format="2">
<container id="stdMultiMap">
<rangeItemRecordType>
<member name="first" templateParameter="0"/>
<member name="second" templateParameter="1"/>
</rangeItemRecordType>
</container>
</def>

View File

@ -1,6 +1,8 @@
<?xml version="1.0"?>
<def format="2">
<smart-pointer class-name="wxObjectDataPtr"/>
<smart-pointer class-name="wxScopedArray"/>
<smart-pointer class-name="wxScopedArray">
<unique/>
</smart-pointer>
<smart-pointer class-name="wxScopedPtr"/>
</def>

View File

@ -19,5 +19,6 @@
<file>files/markup_mandatory_attribute_missing.cfg</file>
<file>files/markup_valid.cfg</file>
<file>files/markup_unhandled_element.cfg</file>
<file>files/container_valid.cfg</file>
</qresource>
</RCC>

View File

@ -182,9 +182,12 @@ void TestCppcheckLibraryData::smartPointerValid()
// Do size and content checks against swapped data.
QCOMPARE(libraryData.smartPointers.size(), 3);
QCOMPARE(libraryData.smartPointers[0], QString("wxObjectDataPtr"));
QCOMPARE(libraryData.smartPointers[1], QString("wxScopedArray"));
QCOMPARE(libraryData.smartPointers[2], QString("wxScopedPtr"));
QCOMPARE(libraryData.smartPointers[0].name, QString("wxObjectDataPtr"));
QCOMPARE(libraryData.smartPointers[0].unique, false);
QCOMPARE(libraryData.smartPointers[1].name, QString("wxScopedArray"));
QCOMPARE(libraryData.smartPointers[1].unique, true);
QCOMPARE(libraryData.smartPointers[2].name, QString("wxScopedPtr"));
QCOMPARE(libraryData.smartPointers[2].unique, false);
// Save library data to file
saveCfgFile(TempCfgFile, libraryData);
@ -199,7 +202,10 @@ void TestCppcheckLibraryData::smartPointerValid()
// Verify no data got lost or modified
QCOMPARE(libraryData.smartPointers.size(), fileLibraryData.smartPointers.size());
QCOMPARE(libraryData.smartPointers.size(), 3);
QCOMPARE(libraryData.smartPointers, fileLibraryData.smartPointers);
for (int idx=0; idx < libraryData.smartPointers.size(); idx++) {
QCOMPARE(libraryData.smartPointers[idx].name, fileLibraryData.smartPointers[idx].name);
QCOMPARE(libraryData.smartPointers[idx].unique, fileLibraryData.smartPointers[idx].unique);
}
}
void TestCppcheckLibraryData::platformTypeValid()
@ -543,6 +549,46 @@ void TestCppcheckLibraryData::markupValid()
}
}
void TestCppcheckLibraryData::containerValid()
{
// Load library data from file
loadCfgFile(":/files/container_valid.cfg", fileLibraryData, result);
QCOMPARE(result.isNull(), true);
// Swap library data read from file to other object
libraryData.swap(fileLibraryData);
// Do size and content checks against swapped data.
QCOMPARE(libraryData.containers.size(), 1);
QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList.size(), 2);
QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].name, QString("first"));
QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[0].templateParameter, QString("0"));
QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].name, QString("second"));
QCOMPARE(libraryData.containers[0].rangeItemRecordTypeList[1].templateParameter, QString("1"));
// Save library data to file
saveCfgFile(TempCfgFile, libraryData);
fileLibraryData.clear();
QCOMPARE(fileLibraryData.containers.size(), 0);
// Reload library data from file
loadCfgFile(TempCfgFile, fileLibraryData, result, true);
QCOMPARE(result.isNull(), true);
// Verify no data got lost or modified
QCOMPARE(libraryData.containers.size(), fileLibraryData.containers.size());
for (int idx=0; idx < libraryData.containers.size(); idx++) {
CppcheckLibraryData::Container lhs = libraryData.containers[idx];
CppcheckLibraryData::Container rhs = fileLibraryData.containers[idx];
for (int num=0; num < lhs.rangeItemRecordTypeList.size(); num++) {
QCOMPARE(lhs.rangeItemRecordTypeList[num].name, rhs.rangeItemRecordTypeList[num].name);
QCOMPARE(lhs.rangeItemRecordTypeList[num].templateParameter, rhs.rangeItemRecordTypeList[num].templateParameter);
}
}
}
void TestCppcheckLibraryData::loadCfgFile(QString filename, CppcheckLibraryData &data, QString &res, bool removeFile)
{
QFile file(filename);

View File

@ -39,6 +39,7 @@ private slots:
void undefineValid();
void reflectionValid();
void markupValid();
void containerValid();
private:
void loadCfgFile(QString filename, CppcheckLibraryData &data, QString &res, bool removeFile = false);

View File

@ -72,7 +72,7 @@ void AnalyzerInformation::close()
}
}
static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long checksum, std::list<ErrorMessage> *errors)
static bool skipAnalysis(const std::string &analyzerInfoFile, std::size_t hash, std::list<ErrorMessage> *errors)
{
tinyxml2::XMLDocument doc;
const tinyxml2::XMLError error = doc.LoadFile(analyzerInfoFile.c_str());
@ -83,8 +83,8 @@ static bool skipAnalysis(const std::string &analyzerInfoFile, unsigned long long
if (rootNode == nullptr)
return false;
const char *attr = rootNode->Attribute("checksum");
if (!attr || attr != std::to_string(checksum))
const char *attr = rootNode->Attribute("hash");
if (!attr || attr != std::to_string(hash))
return false;
for (const tinyxml2::XMLElement *e = rootNode->FirstChildElement(); e; e = e->NextSiblingElement()) {
@ -125,7 +125,7 @@ std::string AnalyzerInformation::getAnalyzerInfoFile(const std::string &buildDir
return filename;
}
bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list<ErrorMessage> *errors)
bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> *errors)
{
if (buildDir.empty() || sourcefile.empty())
return true;
@ -133,13 +133,13 @@ bool AnalyzerInformation::analyzeFile(const std::string &buildDir, const std::st
mAnalyzerInfoFile = AnalyzerInformation::getAnalyzerInfoFile(buildDir,sourcefile,cfg);
if (skipAnalysis(mAnalyzerInfoFile, checksum, errors))
if (skipAnalysis(mAnalyzerInfoFile, hash, errors))
return false;
mOutputStream.open(mAnalyzerInfoFile);
if (mOutputStream.is_open()) {
mOutputStream << "<?xml version=\"1.0\"?>\n";
mOutputStream << "<analyzerinfo checksum=\"" << checksum << "\">\n";
mOutputStream << "<analyzerinfo hash=\"" << hash << "\">\n";
} else {
mAnalyzerInfoFile.clear();
}

View File

@ -54,7 +54,7 @@ public:
/** Close current TU.analyzerinfo file */
void close();
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, unsigned long long checksum, std::list<ErrorMessage> *errors);
bool analyzeFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg, std::size_t hash, std::list<ErrorMessage> *errors);
void reportErr(const ErrorMessage &msg, bool verbose);
void setFileInfo(const std::string &check, const std::string &fileInfo);
static std::string getAnalyzerInfoFile(const std::string &buildDir, const std::string &sourcefile, const std::string &cfg);

View File

@ -865,6 +865,34 @@ bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive)
return false;
}
bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive)
{
const bool pointer = astIsPointer(tok);
const ValueFlow::Value* value = nullptr;
const Token* r = findAstNode(expr, [&](const Token* childTok) {
for (const ValueFlow::Value& val : tok->values()) {
if (val.isImpossible())
continue;
if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) {
if (findAstNode(val.tokvalue,
[&](const Token* aliasTok) {
return aliasTok->exprId() == childTok->exprId();
})) {
if (val.isInconclusive() && inconclusive) { // NOLINT
value = &val;
} else {
return true;
}
}
}
}
return false;
});
if (!r && value && inconclusive)
*inconclusive = true;
return r || value;
}
static bool isAliased(const Token *startTok, const Token *endTok, nonneg int varid)
{
if (!precedes(startTok, endTok))
@ -2452,27 +2480,12 @@ static bool isExpressionChangedAt(const F& getExprTok,
if (globalvar && !tok->isKeyword() && Token::Match(tok, "%name% (") && !(tok->function() && tok->function()->isAttributePure()))
// TODO: Is global variable really changed by function call?
return true;
const bool pointer = astIsPointer(tok);
bool aliased = false;
// If we can't find the expression then assume it is an alias
if (!getExprTok())
aliased = true;
if (!aliased) {
aliased = findAstNode(getExprTok(), [&](const Token* childTok) {
for (const ValueFlow::Value& val : tok->values()) {
if (val.isImpossible())
continue;
if (val.isLocalLifetimeValue() || (pointer && val.isSymbolicValue() && val.intvalue == 0)) {
if (findAstNode(val.tokvalue,
[&](const Token* aliasTok) {
return aliasTok->exprId() == childTok->exprId();
}))
return true;
}
}
return false;
});
}
if (!aliased)
aliased = isAliasOf(tok, getExprTok());
if (!aliased)
return false;
if (isVariableChanged(tok, 1, settings, cpp, depth))

View File

@ -335,6 +335,8 @@ bool isExpressionChangedAt(const Token* expr,
/// If token is an alias if another variable
bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr);
bool isAliasOf(const Token* tok, const Token* expr, bool* inconclusive = nullptr);
bool isAliased(const Variable *var);
const Token* getArgumentStart(const Token* ftok);

View File

@ -739,9 +739,18 @@ void CheckBufferOverrun::stringNotZeroTerminated()
const ValueFlow::Value &bufferSize = getBufferSize(args[0]);
if (bufferSize.intvalue < 0 || sizeToken->getKnownIntValue() < bufferSize.intvalue)
continue;
const Token *srcValue = args[1]->getValueTokenMaxStrLength();
if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue())
continue;
if (Token::simpleMatch(args[1], "(") && Token::simpleMatch(args[1]->astOperand1(), ". c_str") && args[1]->astOperand1()->astOperand1()) {
const std::list<ValueFlow::Value>& contValues = args[1]->astOperand1()->astOperand1()->values();
auto it = std::find_if(contValues.begin(), contValues.end(), [](const ValueFlow::Value& value) {
return value.isContainerSizeValue() && !value.isImpossible();
});
if (it != contValues.end() && it->intvalue < sizeToken->getKnownIntValue())
continue;
} else {
const Token* srcValue = args[1]->getValueTokenMaxStrLength();
if (srcValue && Token::getStrLength(srcValue) < sizeToken->getKnownIntValue())
continue;
}
// Is the buffer zero terminated after the call?
bool isZeroTerminated = false;
for (const Token *tok2 = tok->next()->link(); tok2 != scope->bodyEnd; tok2 = tok2->next()) {

View File

@ -1803,6 +1803,8 @@ void CheckCondition::checkCompareValueOutOfTypeRange()
continue;
if (valueTok->getKnownIntValue() < 0 && valueTok->valueType() && valueTok->valueType()->sign != ValueType::Sign::SIGNED)
continue;
if (valueTok->valueType() && valueTok->valueType()->isTypeEqual(typeTok->valueType()))
continue;
int bits = 0;
switch (typeTok->valueType()->type) {
case ValueType::Type::BOOL:

View File

@ -658,7 +658,7 @@ void CheckFunctions::returnLocalStdMove()
if (retval->variable() && retval->variable()->isLocal() && !retval->variable()->isVolatile())
copyElisionError(retval);
// RVO
if (Token::Match(retval, "(|{") && !retval->isCast())
if (Token::Match(retval, "(|{") && !retval->isCast() && !(retval->valueType() && retval->valueType()->reference != Reference::None))
copyElisionError(retval);
}
}

View File

@ -991,14 +991,16 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope)
// parse the executable scope until tok is reached...
for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
// allocating memory in parameter for function call..
if (!Token::Match(tok, "%name% ("))
if (tok->varId() || !Token::Match(tok, "%name% ("))
continue;
// check if the output of the function is assigned
const Token* tok2 = tok->next()->astParent();
while (tok2 && tok2->isCast())
tok2 = tok2->astParent();
if (Token::Match(tok2, "%assign%|return"))
if (Token::Match(tok2, "%assign%"))
continue;
if (Token::simpleMatch(tok->astTop(), "return"))
continue;
const std::string& functionName = tok->str();
@ -1009,15 +1011,22 @@ void CheckMemoryLeakNoVar::checkForUnreleasedInputArgument(const Scope *scope)
functionName == "return")
continue;
if (Token::simpleMatch(tok->next()->astParent(), "(")) // passed to another function
continue;
if (!tok->isKeyword() && mSettings->library.isNotLibraryFunction(tok))
continue;
if (!CheckMemoryLeakInFunction::test_white_list(functionName, mSettings, mTokenizer->isCPP()))
continue;
const std::vector<const Token *> args = getArguments(tok);
for (const Token* arg : args) {
if (arg->isOp())
if (arg->isOp() && !(tok->isKeyword() && arg->str() == "*")) // e.g. switch (*new int)
continue;
while (arg->astOperand1())
while (arg->astOperand1()) {
if (mTokenizer->isCPP() && Token::simpleMatch(arg, "new"))
break;
arg = arg->astOperand1();
}
if (getAllocationType(arg, 0) == No)
continue;
if (isReopenStandardStream(arg))
@ -1068,7 +1077,13 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope)
if (closingBrace->str() == "}" && Token::Match(closingBrace->link()->tokAt(-1), "%name%") && (!isNew && precedes(tok, closingBrace->link())))
continue;
returnValueNotUsedError(tok, tok->str());
} else if (Token::Match(parent, "%comp%|!")) {
} else if (Token::Match(parent, "%comp%|!|,|%oror%|&&|:")) {
if (parent->astParent() && parent->str() == ",")
continue;
if (parent->str() == ":") {
if (!(Token::simpleMatch(parent->astParent(), "?") && !parent->astParent()->astParent()))
continue;
}
returnValueNotUsedError(tok, tok->str());
}
}

View File

@ -28,6 +28,7 @@
#include "symboldatabase.h"
#include "token.h"
#include "tokenize.h"
#include "utils.h"
#include "valueflow.h"
#include "checkuninitvar.h" // CheckUninitVar::isVariableUsage
@ -1556,6 +1557,8 @@ void CheckOther::checkConstPointer()
deref = true;
else if (Token::simpleMatch(parent, "[") && parent->astOperand1() == tok && tok != nameTok)
deref = true;
else if (Token::Match(parent, "%op%") && Token::simpleMatch(parent->astParent(), "."))
deref = true;
else if (astIsRangeBasedForDecl(tok))
continue;
if (deref) {
@ -1565,16 +1568,19 @@ void CheckOther::checkConstPointer()
if (Token::simpleMatch(gparent, "return"))
continue;
else if (Token::Match(gparent, "%assign%") && parent == gparent->astOperand2()) {
bool takingRef = false;
bool takingRef = false, nonConstPtrAssignment = false;
const Token *lhs = gparent->astOperand1();
if (lhs && lhs->variable() && lhs->variable()->isReference() && lhs->variable()->nameToken() == lhs)
takingRef = true;
if (!takingRef)
if (lhs && lhs->valueType() && lhs->valueType()->pointer && (lhs->valueType()->constness & 1) == 0 &&
parent->valueType() && parent->valueType()->pointer)
nonConstPtrAssignment = true;
if (!takingRef && !nonConstPtrAssignment)
continue;
} else if (Token::simpleMatch(gparent, "[") && gparent->astOperand2() == parent)
continue;
} else {
if (Token::Match(parent, "%oror%|%comp%|&&|?|!"))
if (Token::Match(parent, "%oror%|%comp%|&&|?|!|-"))
continue;
else if (Token::simpleMatch(parent, "(") && Token::Match(parent->astOperand1(), "if|while"))
continue;
@ -1717,7 +1723,7 @@ void CheckOther::charBitOpError(const Token *tok)
static bool isType(const Token * tok, bool unknown)
{
if (Token::Match(tok, "%type%"))
if (tok && (tok->isStandardType() || (!tok->isKeyword() && Token::Match(tok, "%type%"))))
return true;
if (Token::simpleMatch(tok, "::"))
return isType(tok->astOperand2(), unknown);

View File

@ -128,7 +128,7 @@ void CheckStl::outOfBounds()
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
for (const Token *tok = function->bodyStart; tok != function->bodyEnd; tok = tok->next()) {
const Library::Container *container = getLibraryContainer(tok);
if (!container)
if (!container || container->stdAssociativeLike)
continue;
const Token * parent = astParentSkipParens(tok);
const Token* accessTok = parent;

View File

@ -1518,6 +1518,8 @@ void CheckUninitVar::uninitvarError(const Token *tok, const std::string &varname
void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v)
{
if (!mSettings->isEnabled(&v))
return;
if (diag(tok))
return;
const Token* ltok = tok;
@ -1526,13 +1528,15 @@ void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v)
const std::string& varname = ltok ? ltok->expressionString() : "x";
ErrorPath errorPath = v.errorPath;
errorPath.emplace_back(tok, "");
auto severity = v.isKnown() ? Severity::error : Severity::warning;
auto certainty = v.isInconclusive() ? Certainty::inconclusive : Certainty::normal;
if (v.subexpressions.empty()) {
reportError(errorPath,
Severity::error,
severity,
"uninitvar",
"$symbol:" + varname + "\nUninitialized variable: $symbol",
CWE_USE_OF_UNINITIALIZED_VARIABLE,
Certainty::normal);
certainty);
return;
}
std::string vars = v.subexpressions.size() == 1 ? "variable: " : "variables: ";
@ -1542,11 +1546,11 @@ void CheckUninitVar::uninitvarError(const Token* tok, const ValueFlow::Value& v)
prefix = ", ";
}
reportError(errorPath,
Severity::error,
severity,
"uninitvar",
"$symbol:" + varname + "\nUninitialized " + vars,
CWE_USE_OF_UNINITIALIZED_VARIABLE,
Certainty::normal);
certainty);
}
void CheckUninitVar::uninitStructMemberError(const Token *tok, const std::string &membername)

View File

@ -30,6 +30,7 @@
#include "tokenize.h"
#include "tokenlist.h"
#include "utils.h"
#include "valueflow.h"
#include <algorithm>
#include <list>

View File

@ -497,17 +497,21 @@ void clangimport::AstNode::dumpAst(int num, int indent) const
void clangimport::AstNode::setLocations(TokenList *tokenList, int file, int line, int col)
{
for (const std::string &ext: mExtTokens) {
if (ext.compare(0,5,"<col:") == 0)
if (ext.compare(0, 5, "<col:") == 0)
col = std::atoi(ext.substr(5).c_str());
else if (ext.compare(0,6,"<line:") == 0) {
else if (ext.compare(0, 6, "<line:") == 0) {
line = std::atoi(ext.substr(6).c_str());
if (ext.find(", col:") != std::string::npos)
col = std::atoi(ext.c_str() + ext.find(", col:") + 6);
} else if (ext[0] == '<' && ext.find(":") != std::string::npos) {
std::string::size_type sep1 = ext.find(":");
std::string::size_type sep2 = ext.find(":", sep1+1);
file = tokenList->appendFileIfNew(ext.substr(1, sep1 - 1));
line = MathLib::toLongNumber(ext.substr(sep1+1, sep2-sep1));
} else if (ext[0] == '<') {
const std::string::size_type colon = ext.find(':');
if (colon != std::string::npos) {
const bool windowsPath = colon == 2 && ext.size() > 4 && ext[3] == '\\';
std::string::size_type sep1 = windowsPath ? ext.find(':', 4) : colon;
std::string::size_type sep2 = ext.find(':', sep1 + 1);
file = tokenList->appendFileIfNew(ext.substr(1, sep1 - 1));
line = MathLib::toLongNumber(ext.substr(sep1 + 1, sep2 - sep1 - 1));
}
}
}
mFile = file;

View File

@ -112,6 +112,7 @@ static const std::string emptyString;
#define STDCALL
#elif ((defined(__GNUC__) || defined(__sun)) && !defined(__MINGW32__)) || defined(__CPPCHECK__)
#define THREADING_MODEL_FORK
#define STDCALL
#else
#error "No threading model defined"
#endif

View File

@ -707,10 +707,10 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
toolinfo << mSettings.userDefines;
mSettings.nomsg.dump(toolinfo);
// Calculate checksum so it can be compared with old checksum / future checksums
const unsigned int checksum = preprocessor.calculateChecksum(tokens1, toolinfo.str());
// Calculate hash so it can be compared with old hash / future hashes
const std::size_t hash = preprocessor.calculateHash(tokens1, toolinfo.str());
std::list<ErrorMessage> errors;
if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, checksum, &errors)) {
if (!mAnalyzerInformation.analyzeFile(mSettings.buildDir, filename, cfgname, hash, &errors)) {
while (!errors.empty()) {
reportErr(errors.front());
errors.pop_front();
@ -775,7 +775,7 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
}
}
std::set<unsigned long long> checksums;
std::set<unsigned long long> hashes;
int checkCount = 0;
bool hasValidConfig = false;
std::list<std::string> configurationError;
@ -871,18 +871,18 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string
fdump << "</dump>" << std::endl;
}
// Need to call this even if the checksum will skip this configuration
// Need to call this even if the hash will skip this configuration
mSettings.nomsg.markUnmatchedInlineSuppressionsAsChecked(tokenizer);
// Skip if we already met the same simplified token list
if (mSettings.force || mSettings.maxConfigs > 1) {
const unsigned long long checksum = tokenizer.list.calculateChecksum();
if (checksums.find(checksum) != checksums.end()) {
const std::size_t hash = tokenizer.list.calculateHash();
if (hashes.find(hash) != hashes.end()) {
if (mSettings.debugwarnings)
purgedConfigurationMessage(filename, mCurrentConfig);
continue;
}
checksums.insert(checksum);
hashes.insert(hash);
}
// Check normal tokens
@ -1432,7 +1432,12 @@ void CppCheck::executeAddonsWholeProgram(const std::map<std::string, std::size_t
ctuInfoFiles.push_back(getCtuInfoFileName(dumpFileName));
}
executeAddons(ctuInfoFiles);
try {
executeAddons(ctuInfoFiles);
} catch (const InternalError& e) {
internalError("", "Internal error during whole program analysis: " + e.errorMessage);
mExitCode = 1;
}
for (const std::string &f: ctuInfoFiles) {
std::remove(f.c_str());

View File

@ -29,6 +29,7 @@
#include "tokenlist.h"
#include "utils.h"
#include <algorithm>
#include <cstdint>
#include <cstring>
#include <iterator> // back_inserter

View File

@ -28,7 +28,6 @@
#include "mathlib.h"
#include "valueflow.h"
#include <algorithm>
#include <list>
#include <map>
#include <string>

View File

@ -368,7 +368,7 @@ bool ErrorMessage::deserialize(const std::string &data)
return true;
}
std::string ErrorMessage::getXMLHeader()
std::string ErrorMessage::getXMLHeader(const std::string& productName)
{
tinyxml2::XMLPrinter printer;
@ -380,6 +380,8 @@ std::string ErrorMessage::getXMLHeader()
printer.PushAttribute("version", 2);
printer.OpenElement("cppcheck", false);
if (!productName.empty())
printer.PushAttribute("product-name", productName.c_str());
printer.PushAttribute("version", CppCheck::version());
printer.CloseElement(false);
printer.OpenElement("errors", false);
@ -489,7 +491,7 @@ static std::string readCode(const std::string &file, int linenr, int column, con
std::string::size_type pos = 0;
while ((pos = line.find('\t', pos)) != std::string::npos)
line[pos] = ' ';
return line + endl + std::string((column>0 ? column-1 : column), ' ') + '^';
return line + endl + std::string((column>0 ? column-1 : 0), ' ') + '^';
}
static void replaceColors(std::string& source)

View File

@ -160,7 +160,7 @@ public:
*/
std::string toXML() const;
static std::string getXMLHeader();
static std::string getXMLHeader(const std::string& productName);
static std::string getXMLFooter();
/**

View File

@ -1037,23 +1037,6 @@ std::string MathLib::subtract(const std::string &first, const std::string &secon
#endif
}
std::string MathLib::incdec(const std::string & var, const std::string & op)
{
#ifdef TEST_MATHLIB_VALUE
if (op == "++")
return value(var).add(1).str();
else if (op == "--")
return value(var).add(-1).str();
#else
if (op == "++")
return MathLib::add(var, "1");
else if (op == "--")
return MathLib::subtract(var, "1");
#endif
throw InternalError(nullptr, std::string("Unexpected operation '") + op + "' in MathLib::incdec(). Please report this to Cppcheck developers.");
}
std::string MathLib::divide(const std::string &first, const std::string &second)
{
#ifdef TEST_MATHLIB_VALUE

View File

@ -106,7 +106,6 @@ public:
static std::string multiply(const std::string & first, const std::string & second);
static std::string divide(const std::string & first, const std::string & second);
static std::string mod(const std::string & first, const std::string & second);
static std::string incdec(const std::string & var, const std::string & op);
static std::string calculate(const std::string & first, const std::string & second, char action);
static std::string sin(const std::string & tok);

View File

@ -23,7 +23,6 @@
#include "utils.h"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <fstream>

View File

@ -21,8 +21,6 @@
#include "path.h"
#include "utils.h"
#include <algorithm>
#include <cctype>
#include <cstddef>
PathMatch::PathMatch(const std::vector<std::string> &excludedPaths, bool caseSensitive)

View File

@ -30,6 +30,7 @@
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <iterator> // back_inserter
#include <memory>
#include <utility>
@ -971,74 +972,20 @@ void Preprocessor::dump(std::ostream &out) const
}
}
static const std::uint32_t crc32Table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
};
static void crc32(const std::string &data, uint32_t& crc)
std::size_t Preprocessor::calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
{
for (char c : data) {
crc = crc32Table[(crc ^ (unsigned char)c) & 0xFF] ^ (crc >> 8);
}
}
uint32_t Preprocessor::calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const
{
std::uint32_t crc = ~0U;
crc32(toolinfo, crc);
std::string hashData = toolinfo;
for (const simplecpp::Token *tok = tokens1.cfront(); tok; tok = tok->next) {
if (!tok->comment)
crc32(tok->str(), crc);
hashData += tok->str();
}
for (std::map<std::string, simplecpp::TokenList *>::const_iterator it = mTokenLists.begin(); it != mTokenLists.end(); ++it) {
for (const simplecpp::Token *tok = it->second->cfront(); tok; tok = tok->next) {
if (!tok->comment)
crc32(tok->str(), crc);
hashData += tok->str();
}
}
return crc ^ ~0U;
return (std::hash<std::string>{})(hashData);
}
void Preprocessor::simplifyPragmaAsm(simplecpp::TokenList *tokenList) const

View File

@ -164,13 +164,13 @@ public:
void validateCfgError(const std::string &file, const unsigned int line, const std::string &cfg, const std::string &macro);
/**
* Calculate CRC32 checksum. Using toolinfo, tokens1, filedata.
* Calculate HASH. Using toolinfo, tokens1, filedata.
*
* @param tokens1 Sourcefile tokens
* @param toolinfo Arbitrary extra toolinfo
* @return CRC32 checksum
* @return HASH
*/
uint32_t calculateChecksum(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const;
std::size_t calculateHash(const simplecpp::TokenList &tokens1, const std::string &toolinfo) const;
void simplifyPragmaAsm(simplecpp::TokenList *tokenList) const;

View File

@ -632,6 +632,8 @@ static ValueFlow::Value executeImpl(const Token* expr, ProgramMemory& pm, const
if (MathLib::isFloat(expr->str()))
return unknown;
return ValueFlow::Value{MathLib::toLongNumber(expr->str())};
} else if (expr->isBoolean()) {
return ValueFlow::Value{ expr->str() == "true" };
} else if (Token::Match(expr->tokAt(-2), ". %name% (") && astIsContainer(expr->tokAt(-2)->astOperand1())) {
const Token* containerTok = expr->tokAt(-2)->astOperand1();
Library::Container::Yield yield = containerTok->valueType()->container->getYield(expr->strAt(-1));

View File

@ -6508,7 +6508,7 @@ static const Token* parsedecl(const Token* type,
} else if (const Library::SmartPointer* smartPointer = settings->library.detectSmartPointer(type)) {
const Token* argTok = Token::findsimplematch(type, "<");
if (!argTok)
continue;
break;
valuetype->smartPointer = smartPointer;
valuetype->smartPointerTypeToken = argTok->next();
valuetype->smartPointerType = argTok->next()->type();

View File

@ -3791,8 +3791,10 @@ void TemplateSimplifier::simplifyTemplates(
specializations,
maxtime,
expandedtemplates);
if (instantiated)
if (instantiated) {
mInstantiatedTemplates.push_back(*iter1);
mTemplateNamePos.clear(); // positions might be invalid after instantiations
}
}
for (std::list<TokenAndName>::const_iterator it = mInstantiatedTemplates.begin(); it != mInstantiatedTemplates.end(); ++it) {

File diff suppressed because it is too large Load Diff

View File

@ -159,23 +159,6 @@ public:
void splitTemplateRightAngleBrackets(bool check);
/**
* Deletes dead code between 'begin' and 'end'.
* In general not everything can be erased, such as:
* - code after labels;
* - code outside the scope where the function is called;
* - code after a change of scope caused by 'switch(...);'
* instructions, like 'case %any%;' or 'default;'
* Also, if the dead code contains a 'switch' block
* and inside it there's a label, the function removes all
* the 'switch(..)' tokens and every occurrence of 'case %any%; | default;'
* expression, such as the 'switch' block is reduced to a simple block.
*
* @param begin Tokens after this have a possibility to be erased.
* @param end Tokens before this have a possibility to be erased.
*/
static void eraseDeadCode(Token *begin, const Token *end);
/**
* Calculates sizeof value for given type.
* @param type Token which will contain e.g. "int", "*", or string.
@ -196,13 +179,6 @@ public:
/** Simplify assignment where rhs is a block : "x=({123;});" => "{x=123;}" */
void simplifyAssignmentBlock();
/**
* Simplify constant calculations such as "1+2" => "3"
* @return true if modifications to token-list are done.
* false if no modifications are done.
*/
bool simplifyCalculations();
/** Insert array size where it isn't given */
void arraySize();
@ -329,35 +305,9 @@ public:
*/
bool simplifyUsing();
/**
* A simplify function that replaces a variable with its value in cases
* when the value is known. e.g. "x=10; if(x)" => "x=10;if(10)"
*
* @return true if modifications to token-list are done.
* false if no modifications are done.
*/
bool simplifyKnownVariables();
/**
* Utility function for simplifyKnownVariables. Get data about an
* assigned variable.
*/
static bool simplifyKnownVariablesGetData(nonneg int varid, Token **_tok2, Token **_tok3, std::string &value, nonneg int &valueVarId, bool &valueIsPointer, bool floatvar);
/**
* utility function for simplifyKnownVariables. Perform simplification
* of a given variable
*/
bool simplifyKnownVariablesSimplify(Token **tok2, Token *tok3, nonneg int varid, const std::string &structname, std::string &value, nonneg int valueVarId, bool valueIsPointer, const Token * const valueToken, int indentlevel) const;
/** Simplify useless C++ empty namespaces, like: 'namespace %name% { }'*/
void simplifyEmptyNamespaces();
/** Simplify redundant code placed after control flow statements :
* 'return', 'throw', 'goto', 'break' and 'continue'
*/
void simplifyFlowControl();
/** Simplify "if else" */
void elseif();

View File

@ -461,27 +461,17 @@ void TokenList::createTokens(simplecpp::TokenList&& tokenList)
//---------------------------------------------------------------------------
uint64_t TokenList::calculateChecksum() const
std::size_t TokenList::calculateHash() const
{
uint64_t checksum = 0;
std::string hashData;
for (const Token* tok = front(); tok; tok = tok->next()) {
const uint32_t subchecksum1 = tok->flags() + tok->varId() + tok->tokType();
uint32_t subchecksum2 = 0;
for (char i : tok->str())
subchecksum2 += (uint32_t)i;
if (!tok->originalName().empty()) {
for (char i : tok->originalName())
subchecksum2 += (uint32_t)i;
}
checksum ^= ((static_cast<uint64_t>(subchecksum1) << 32) | subchecksum2);
const bool bit1 = (checksum & 1) != 0;
checksum >>= 1;
if (bit1)
checksum |= (1ULL << 63);
hashData += MathLib::toString(tok->flags());
hashData += MathLib::toString(tok->varId());
hashData += MathLib::toString(tok->tokType());
hashData += tok->str();
hashData += tok->originalName();
}
return checksum;
return (std::hash<std::string>{})(hashData);
}

View File

@ -153,10 +153,10 @@ public:
std::string fileLine(const Token *tok) const;
/**
* Calculates a 64-bit checksum of the token list used to compare
* multiple token lists with each other as quickly as possible.
* Calculates a hash of the token list used to compare multiple
* token lists with each other as quickly as possible.
*/
uint64_t calculateChecksum() const;
std::size_t calculateHash() const;
/**
* Create abstract syntax tree.

View File

@ -986,8 +986,7 @@ static void setTokenValue(Token* tok,
ValueFlow::Value v(val);
v.intvalue = ~v.intvalue;
int bits = 0;
if (settings &&
tok->valueType() &&
if (tok->valueType() &&
tok->valueType()->sign == ValueType::Sign::UNSIGNED &&
tok->valueType()->pointer == 0) {
if (tok->valueType()->type == ValueType::Type::INT)

View File

@ -25,9 +25,7 @@ if (BUILD_TESTS)
if(tinyxml2_FOUND AND NOT USE_BUNDLED_TINYXML2)
target_link_libraries(testrunner ${tinyxml2_LIBRARIES})
endif()
if (USE_THREADS)
target_link_libraries(testrunner ${CMAKE_THREAD_LIBS_INIT})
endif()
target_link_libraries(testrunner ${CMAKE_THREAD_LIBS_INIT})
if (NOT CMAKE_DISABLE_PRECOMPILE_HEADERS)
target_precompile_headers(testrunner PRIVATE precompiled.h)

View File

@ -71,8 +71,8 @@ TEST(Test, warning_in_assert_macros)
ASSERT_GE(i, i);
unsigned int u = errno;
// cppcheck-suppress [unsignedPositive, compareValueOutOfTypeRangeError]
// cppcheck-suppress [unsignedPositive]
ASSERT_GE(u, 0);
// cppcheck-suppress [unsignedLessThanZero, compareValueOutOfTypeRangeError]
// cppcheck-suppress [unsignedLessThanZero]
ASSERT_LT(u, 0);
}

View File

@ -22,12 +22,27 @@
#include <wx/memory.h>
#include <wx/frame.h>
#include <wx/menu.h>
#include <wx/regex.h>
#include <wx/stattext.h>
#include <wx/sizer.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/propgrid/property.h>
void uninitvar_wxRegEx_GetMatch(wxRegEx &obj, size_t *start, size_t *len, size_t index)
{
size_t s,l;
size_t *sPtr,*lPtr;
// cppcheck-suppress uninitvar
(void)obj.GetMatch(&s,lPtr);
// TODO cppcheck-suppress uninitvar
(void)obj.GetMatch(sPtr,&l);
(void)obj.GetMatch(&s,&l);
(void)obj.GetMatch(start,len);
(void)obj.GetMatch(start,len,0);
(void)obj.GetMatch(start,len,index);
}
#ifdef __VISUALC__
// Ensure no duplicateBreak warning is issued after wxLogApiError() calls.
// This function does not terminate execution.

View File

@ -191,6 +191,7 @@ private:
TEST_CASE(array_index_64); // #10878
TEST_CASE(array_index_65); // #11066
TEST_CASE(array_index_66); // #10740
TEST_CASE(array_index_67); // #1596
TEST_CASE(array_index_multidim);
TEST_CASE(array_index_switch_in_for);
TEST_CASE(array_index_for_in_for); // FP: #2634
@ -288,6 +289,7 @@ private:
TEST_CASE(terminateStrncpy2);
TEST_CASE(terminateStrncpy3);
TEST_CASE(terminateStrncpy4);
TEST_CASE(terminateStrncpy5); // #9944
TEST_CASE(recursive_long_time);
TEST_CASE(crash1); // Ticket #1587 - crash
@ -1852,6 +1854,25 @@ private:
errout.str());
}
void array_index_67() {
check("void func(int i) {\n" // #1596
" int types[3];\n"
" int type_cnt = 0;\n"
" if (i == 0) {\n"
" types[type_cnt] = 0;\n"
" type_cnt++;\n"
" types[type_cnt] = 0;\n"
" type_cnt++;\n"
" types[type_cnt] = 0;\n"
" type_cnt++;\n"
" } else {\n"
" types[type_cnt] = 1;\n"
" type_cnt++;\n"
" }\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void array_index_multidim() {
check("void f()\n"
"{\n"
@ -4322,6 +4343,23 @@ private:
"}");
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) The buffer 'buf' may not be null-terminated after the call to strncpy().\n", errout.str());
}
void terminateStrncpy5() { // #9944
check("void f(const std::string& buf) {\n"
" char v[255];\n"
" if (buf.size() >= sizeof(v))\n"
" return;\n"
" strncpy(v, buf.c_str(), sizeof(v));\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("void f(const std::string& buf) {\n"
" char v[255];\n"
" if (buf.size() >= sizeof(v))\n"
" strncpy(v, buf.c_str(), sizeof(v));\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (warning, inconclusive) The buffer 'v' may not be null-terminated after the call to strncpy().\n", errout.str());
}
// extracttests.enable
void recursive_long_time() {

View File

@ -584,7 +584,10 @@ private:
}
void cxxRecordDecl1() {
const char clang[] = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo";
const char* clang = "`-CXXRecordDecl 0x34cc5f8 <1.cpp:2:1, col:7> col:7 class Foo";
ASSERT_EQUALS("class Foo ;", parse(clang));
clang = "`-CXXRecordDecl 0x34cc5f8 <C:\\Foo\\Bar Baz\\1.cpp:2:1, col:7> col:7 class Foo";
ASSERT_EQUALS("class Foo ;", parse(clang));
}

View File

@ -5258,6 +5258,12 @@ private:
"}", &settingsUnix64);
ASSERT_EQUALS("", errout.str());
check("int f(int x) {\n"
" const int i = 0xFFFFFFFF;\n"
" if (x == i) {}\n"
"}", &settingsUnix64);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char c;\n"
" if ((c = foo()) != -1) {}\n"

View File

@ -192,7 +192,7 @@ private:
header += " <cppcheck version=\"";
header += CppCheck::version();
header += "\"/>\n <errors>";
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader());
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
std::string message(" <error id=\"errorId\" severity=\"error\"");
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";
@ -208,7 +208,7 @@ private:
header += " <cppcheck version=\"";
header += CppCheck::version();
header += "\"/>\n <errors>";
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader());
ASSERT_EQUALS(header, ErrorMessage::getXMLHeader(""));
ASSERT_EQUALS(" </errors>\n</results>", ErrorMessage::getXMLFooter());
std::string message(" <error id=\"errorId\" severity=\"error\"");
message += " msg=\"Programming error.\" verbose=\"Verbose error\">\n";

View File

@ -1731,6 +1731,18 @@ private:
check("struct A{} a; A f1() { return std::move(a); }\n"
"A f2() { volatile A var; return std::move(var); }");
ASSERT_EQUALS("", errout.str());
check("struct S { std::string msg{ \"abc\" }; };\n"
"std::unique_ptr<S> get(std::vector<std::unique_ptr<S>>& v) {\n"
" return std::move(v.front());\n"
"}\n"
"int main() {\n"
" std::vector<std::unique_ptr<S>> v;\n"
" v.emplace_back(std::make_unique<S>());\n"
" auto p = get(v);\n"
" std::cout << p->msg;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void negativeMemoryAllocationSizeError() { // #389

View File

@ -662,6 +662,11 @@ private:
" +\" y = \" + b;\n"
"}\n", /*inconclusive*/ true);
ASSERT_EQUALS("[test.cpp:3]: (warning, inconclusive) Found suspicious operator '+', result is not used.\n", errout.str());
check("void f() {\n"
" *new int;\n"
"}\n", /*inconclusive*/ true);
ASSERT_EQUALS("[test.cpp:2]: (warning, inconclusive) Found suspicious operator '*', result is not used.\n", errout.str());
}
void vardecl() {

View File

@ -93,6 +93,7 @@ private:
TEST_CASE(assign21); // #10186
TEST_CASE(assign22); // #9139
TEST_CASE(assign23);
TEST_CASE(assign24); // #7440
TEST_CASE(isAutoDealloc);
@ -504,6 +505,23 @@ private:
settings = s;
}
void assign24() { // #7440
check("void f() {\n"
" char* data = new char[100];\n"
" char** dataPtr = &data;\n"
" delete[] *dataPtr;\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" char* data = new char[100];\n"
" char** dataPtr = &data;\n"
" printf(\"test\");\n"
" delete[] *dataPtr;\n"
"}\n", true);
ASSERT_EQUALS("", errout.str());
}
void isAutoDealloc() {
check("void f() {\n"
" char *p = new char[100];"

View File

@ -57,7 +57,6 @@ private:
TEST_CASE(toDoubleNumber);
TEST_CASE(naninf);
TEST_CASE(isNullValue);
TEST_CASE(incdec);
TEST_CASE(sin);
TEST_CASE(cos);
TEST_CASE(tan);
@ -1192,27 +1191,6 @@ private:
ASSERT_EQUALS(false, MathLib::isNullValue("-ENOMEM"));
}
void incdec() const {
// increment
{
const MathLib::biguint num = ~10U;
const std::string op = "++";
const std::string strNum = MathLib::incdec(MathLib::toString(num), op);
const MathLib::biguint incrementedNum = MathLib::toULongNumber(strNum);
ASSERT_EQUALS(num + 1U, incrementedNum);
}
// decrement
{
const MathLib::biguint num = ~10U;
const std::string op = "--";
const std::string strNum = MathLib::incdec(MathLib::toString(num), op);
const MathLib::biguint decrementedNum = MathLib::toULongNumber(strNum);
ASSERT_EQUALS(num - 1U, decrementedNum);
}
// invalid operation
ASSERT_THROW(MathLib::incdec("1", "x"), InternalError); // throw
}
void sin() const {
ASSERT_EQUALS("0.0", MathLib::sin("0"));
}

View File

@ -2401,6 +2401,38 @@ private:
" return static_cast<int*>(malloc(size));\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f() { if (new int[42]) {} }\n" // #10857
"void g() { if (malloc(42)) {} }\n");
ASSERT_EQUALS("[test.cpp:1]: (error) Allocation with new, if doesn't release it.\n"
"[test.cpp:2]: (error) Allocation with malloc, if doesn't release it.\n",
errout.str());
check("const char* string(const char* s) {\n"
" StringSet::iterator it = strings_.find(s);\n"
" if (it != strings_.end())\n"
" return *it;\n"
" return *strings_.insert(it, std::strcpy(new char[std::strlen(s) + 1], s));\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("struct S {\n"
" static void load(const QString& projPath) {\n"
" if (proj_)\n"
" return;\n"
" proj_ = new ProjectT(projPath);\n"
" proj_->open(new OpenCallback());\n"
" }\n"
"private:\n"
" static Core::ProjectBase* proj_;\n"
"};\n");
ASSERT_EQUALS("", errout.str());
check("void f(const std::string& s, int n) {\n"
" std::unique_ptr<char[]> u;\n"
" u.reset(strcpy(new char[n], s.c_str()));\n"
"};\n");
ASSERT_EQUALS("", errout.str());
}
void missingAssignment() {
@ -2452,7 +2484,7 @@ private:
"{\n"
" 42,malloc(42);\n"
"}");
TODO_ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", "", errout.str());
ASSERT_EQUALS("[test.cpp:3]: (error) Return value of allocation function 'malloc' is not stored.\n", errout.str());
check("void *f()\n"
"{\n"
@ -2595,6 +2627,33 @@ private:
" C{ new QWidget, 1 };\n"
"}");
ASSERT_EQUALS("", errout.str());
check("void f(bool b) { if (b && malloc(42)) {} }\n" // // #10858
"void g(bool b) { if (b || malloc(42)) {} }\n");
ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'malloc' is not stored.\n"
"[test.cpp:2]: (error) Return value of allocation function 'malloc' is not stored.\n",
errout.str());
check("void f0(const bool b) { b ? new int : nullptr; }\n" // #11155
"void f1(const bool b) { b ? nullptr : new int; }\n"
"int* g0(const bool b) { return b ? new int : nullptr; }\n"
"void g1(const bool b) { h(b, b ? nullptr : new int); }\n");
ASSERT_EQUALS("[test.cpp:1]: (error) Return value of allocation function 'new' is not stored.\n"
"[test.cpp:2]: (error) Return value of allocation function 'new' is not stored.\n",
errout.str());
check("void f() {\n" // #11157
" switch (*new int) { case 42: break; }\n"
" switch (*malloc(42)) { case 42: break; }\n"
"}\n");
ASSERT_EQUALS("[test.cpp:2]: (error) Allocation with new, switch doesn't release it.\n"
"[test.cpp:3]: (error) Allocation with malloc, switch doesn't release it.\n",
errout.str());
check("void f() {\n"
" Ref<StringBuffer> remove(new StringBuffer());\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void smartPointerFunctionParam() {

View File

@ -64,6 +64,7 @@ private:
TEST_CASE(zeroDiv11);
TEST_CASE(zeroDiv12);
TEST_CASE(zeroDiv13);
TEST_CASE(zeroDiv14); // #1169
TEST_CASE(zeroDivCond); // division by zero / useless condition
@ -587,6 +588,17 @@ private:
ASSERT_EQUALS("[test.cpp:4]: (error) Division by zero.\n", errout.str());
}
void zeroDiv14() {
check("void f() {\n" // #1169
" double dx = 1.;\n"
" int ix = 1;\n"
" int i = 1;\n"
" std::cout << ix / (i >> 1) << std::endl;\n"
" std::cout << dx / (i >> 1) << std::endl;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:5]: (error) Division by zero.\n", errout.str());
}
void zeroDivCond() {
check("void f(unsigned int x) {\n"
" int y = 17 / x;\n"
@ -3230,6 +3242,21 @@ private:
ASSERT_EQUALS("[test.cpp:2]: (style) Variable 'p' can be declared as pointer to const\n"
"[test.cpp:5]: (style) Variable 'p' can be declared as pointer to const\n",
errout.str());
check("void f() {\n"
" char a[1][1];\n"
" char* b[1];\n"
" b[0] = a[0];\n"
" **b = 0;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
check("ptrdiff_t f(int *p0, int *p1) {\n" // #11148
" return p0 - p1;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:1]: (style) Parameter 'p0' can be declared as pointer to const\n"
"[test.cpp:1]: (style) Parameter 'p1' can be declared as pointer to const\n",
errout.str());
}
void switchRedundantAssignmentTest() {
@ -9383,7 +9410,7 @@ private:
" int local_argc = 0;\n"
" local_argv[local_argc++] = argv[0];\n"
"}\n", "test.c");
ASSERT_EQUALS("[test.c:1]: (style) Parameter 'argv' can be declared as const array\n", errout.str());
ASSERT_EQUALS("", errout.str());
check("void f() {\n"
" int x = 0;\n"

View File

@ -0,0 +1,142 @@
/*
* Cppcheck - A tool for static C/C++ code analysis
* Copyright (C) 2007-2022 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 <http://www.gnu.org/licenses/>.
*/
#include "processexecutor.h"
#include "settings.h"
#include "testsuite.h"
#include "testutils.h"
#include <algorithm>
#include <cstddef>
#include <map>
#include <ostream>
#include <string>
#include <utility>
#include <vector>
class TestProcessExecutor : public TestFixture {
public:
TestProcessExecutor() : TestFixture("TestProcessExecutor") {}
private:
Settings settings;
/**
* Execute check using n jobs for y files which are have
* identical data, given within data.
*/
void check(unsigned int jobs, int files, int result, const std::string &data) {
errout.str("");
output.str("");
std::map<std::string, std::size_t> filemap;
for (int i = 1; i <= files; ++i) {
std::ostringstream oss;
oss << "file_" << i << ".cpp";
filemap[oss.str()] = data.size();
}
settings.jobs = jobs;
ProcessExecutor executor(filemap, settings, *this);
std::vector<ScopedFile> scopedfiles;
scopedfiles.reserve(filemap.size());
for (std::map<std::string, std::size_t>::const_iterator i = filemap.begin(); i != filemap.end(); ++i)
scopedfiles.emplace_back(i->first, data);
ASSERT_EQUALS(result, executor.check());
}
void run() override {
#if !defined(WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
LOAD_LIB_2(settings.library, "std.cfg");
TEST_CASE(deadlock_with_many_errors);
TEST_CASE(many_threads);
TEST_CASE(no_errors_more_files);
TEST_CASE(no_errors_less_files);
TEST_CASE(no_errors_equal_amount_files);
TEST_CASE(one_error_less_files);
TEST_CASE(one_error_several_files);
#endif // !WIN32
}
void deadlock_with_many_errors() {
std::ostringstream oss;
oss << "int main()\n"
<< "{\n";
for (int i = 0; i < 500; i++)
oss << " {char *a = malloc(10);}\n";
oss << " return 0;\n"
<< "}\n";
check(2, 3, 3, oss.str());
}
void many_threads() {
check(16, 100, 100,
"int main()\n"
"{\n"
" char *a = malloc(10);\n"
" return 0;\n"
"}");
}
void no_errors_more_files() {
check(2, 3, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void no_errors_less_files() {
check(2, 1, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void no_errors_equal_amount_files() {
check(2, 2, 0,
"int main()\n"
"{\n"
" return 0;\n"
"}");
}
void one_error_less_files() {
check(2, 1, 1,
"int main()\n"
"{\n"
" {char *a = malloc(10);}\n"
" return 0;\n"
"}");
}
void one_error_several_files() {
check(2, 20, 20,
"int main()\n"
"{\n"
" {char *a = malloc(10);}\n"
" return 0;\n"
"}");
}
};
REGISTER_TEST(TestProcessExecutor)

View File

@ -27,6 +27,8 @@
<ClCompile Include="..\cli\cmdlineparser.cpp" />
<ClCompile Include="..\cli\cppcheckexecutor.cpp" />
<ClCompile Include="..\cli\filelister.cpp" />
<ClCompile Include="..\cli\executor.cpp" />
<ClCompile Include="..\cli\processexecutor.cpp" />
<ClCompile Include="..\cli\threadexecutor.cpp" />
<ClCompile Include="..\lib\astutils.cpp" />
<ClCompile Include="options.cpp" />
@ -64,6 +66,7 @@
<ClCompile Include="testplatform.cpp" />
<ClCompile Include="testpostfixoperator.cpp" />
<ClCompile Include="testpreprocessor.cpp" />
<ClCompile Include="testprocessexecutor.cpp" />
<ClCompile Include="testrunner.cpp" />
<ClCompile Include="testsimplifytemplate.cpp" />
<ClCompile Include="testsimplifytokens.cpp" />

View File

@ -218,6 +218,7 @@ private:
TEST_CASE(template173); // #10332 crash
TEST_CASE(template174); // #10506 hang
TEST_CASE(template175); // #10908
TEST_CASE(template176); // #11146
TEST_CASE(template_specialization_1); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_specialization_2); // #7868 - template specialization template <typename T> struct S<C<T>> {..};
TEST_CASE(template_enum); // #6299 Syntax error in complex enum declaration (including template)
@ -4458,7 +4459,7 @@ private:
ASSERT_EQUALS(exp, tok(code));
}
void template175()
void template175() // #10908
{
const char code[] = "template <typename T, int value> T Get() {return value;}\n"
"char f() { Get<int,10>(); }\n";
@ -4468,6 +4469,27 @@ private:
ASSERT_EQUALS(exp, tok(code));
}
void template176() // #11146 don't crash
{
const char code[] = "struct a {\n"
" template <typename> class b {};\n"
"};\n"
"struct c {\n"
" template <typename> a::b<int> d();\n"
" ;\n"
"};\n"
"template <typename> a::b<int> c::d() {}\n"
"template <> class a::b<int> c::d<int>() { return {}; };\n";
const char exp[] = "struct a { "
"class b<int> c :: d<int> ( ) ; "
"template < typename > class b { } ; "
"} ; "
"struct c { a :: b<int> d<int> ( ) ; } ; "
"class a :: b<int> c :: d<int> ( ) { return { } ; } ; "
"a :: b<int> c :: d<int> ( ) { }";
ASSERT_EQUALS(exp, tok(code));
}
void template_specialization_1() { // #7868 - template specialization template <typename T> struct S<C<T>> {..};
const char code[] = "template <typename T> struct C {};\n"
"template <typename T> struct S {a};\n"

View File

@ -62,7 +62,6 @@ private:
TEST_CASE(double_plus);
TEST_CASE(redundant_plus);
TEST_CASE(redundant_plus_numbers);
TEST_CASE(parentheses1);
TEST_CASE(declareVar);
TEST_CASE(declareArray);
@ -90,20 +89,13 @@ private:
// Simplify "?:"
TEST_CASE(simplifyConditionOperator);
// Simplify calculations
TEST_CASE(calculations);
TEST_CASE(comparisons);
TEST_CASE(simplifyCalculations);
TEST_CASE(simplifyOperator1);
TEST_CASE(simplifyOperator2);
TEST_CASE(simplifyArrayAccessSyntax);
TEST_CASE(pointeralias1);
TEST_CASE(pointeralias2);
TEST_CASE(pointeralias3);
TEST_CASE(pointeralias4);
// struct ABC { } abc; => struct ABC { }; ABC abc;
TEST_CASE(simplifyStructDecl1);
@ -137,58 +129,35 @@ private:
TEST_CASE(simplifyNamespaceAliases1);
TEST_CASE(simplifyNamespaceAliases2); // ticket #10281
TEST_CASE(simplifyKnownVariables1);
TEST_CASE(simplifyKnownVariables2);
TEST_CASE(simplifyKnownVariables3);
TEST_CASE(simplifyKnownVariables4);
TEST_CASE(simplifyKnownVariables5);
TEST_CASE(simplifyKnownVariables6);
TEST_CASE(simplifyKnownVariables7);
TEST_CASE(simplifyKnownVariables8);
TEST_CASE(simplifyKnownVariables9);
TEST_CASE(simplifyKnownVariables10);
TEST_CASE(simplifyKnownVariables11);
TEST_CASE(simplifyKnownVariables13);
TEST_CASE(simplifyKnownVariables14);
TEST_CASE(simplifyKnownVariables15);
TEST_CASE(simplifyKnownVariables16);
TEST_CASE(simplifyKnownVariables17);
TEST_CASE(simplifyKnownVariables18);
TEST_CASE(simplifyKnownVariables19);
TEST_CASE(simplifyKnownVariables20);
TEST_CASE(simplifyKnownVariables21);
TEST_CASE(simplifyKnownVariables22);
TEST_CASE(simplifyKnownVariables23);
TEST_CASE(simplifyKnownVariables25);
TEST_CASE(simplifyKnownVariables27);
TEST_CASE(simplifyKnownVariables28);
// FIXME Does expression id handle these? TEST_CASE(simplifyKnownVariables29); // ticket #1811
TEST_CASE(simplifyKnownVariables30);
TEST_CASE(simplifyKnownVariables31);
TEST_CASE(simplifyKnownVariables32); // const
TEST_CASE(simplifyKnownVariables33); // struct variable
TEST_CASE(simplifyKnownVariables34);
TEST_CASE(simplifyKnownVariables36); // ticket #2304 - known value for strcpy parameter
TEST_CASE(simplifyKnownVariables39);
TEST_CASE(simplifyKnownVariables41); // p=&x; if (p) ..
TEST_CASE(simplifyKnownVariables42); // ticket #2031 - known string value after strcpy
TEST_CASE(simplifyKnownVariables43);
TEST_CASE(simplifyKnownVariables44); // ticket #3117 - don't simplify static variables
TEST_CASE(simplifyKnownVariables45); // ticket #3281 - static constant variable not simplified
TEST_CASE(simplifyKnownVariables46); // ticket #3587 - >>
TEST_CASE(simplifyKnownVariables47); // ticket #3627 - >>
TEST_CASE(simplifyKnownVariables48); // ticket #3754 - wrong simplification in for loop header
TEST_CASE(simplifyKnownVariables49); // #3691 - continue in switch
TEST_CASE(simplifyKnownVariables50); // #4066 sprintf changes
TEST_CASE(simplifyKnownVariables51); // #4409 hang
TEST_CASE(simplifyKnownVariables53); // references
TEST_CASE(simplifyKnownVariables54); // #4913 'x' is not 0 after *--x=0;
TEST_CASE(simplifyKnownVariables55); // pointer alias
TEST_CASE(simplifyKnownVariables56); // ticket #5301 - >>
TEST_CASE(simplifyKnownVariables57); // ticket #4724
TEST_CASE(simplifyKnownVariables58); // ticket #5268
TEST_CASE(simplifyKnownVariables59); // skip for header
TEST_CASE(simplifyKnownVariables60); // #6829
TEST_CASE(simplifyKnownVariables61); // #7805
TEST_CASE(simplifyKnownVariables62); // #5666 - p=&str[0]
TEST_CASE(simplifyKnownVariables63); // #10798
@ -203,11 +172,8 @@ private:
TEST_CASE(simplifyKnownVariablesClassMember); // #2815 - value of class member may be changed by function call
TEST_CASE(simplifyKnownVariablesFunctionCalls); // Function calls (don't assume pass by reference)
TEST_CASE(simplifyKnownVariablesGlobalVars);
TEST_CASE(simplifyKnownVariablesPointerAliasFunctionCall); // #7440
TEST_CASE(simplifyKnownVariablesNamespace); // #10059
TEST_CASE(simplify_constants2);
TEST_CASE(simplify_constants4);
TEST_CASE(simplify_constants6); // Ticket #5625: Ternary operator as template parameter
TEST_CASE(simplifyVarDeclInitLists);
}
@ -1887,11 +1853,6 @@ private:
}
void parentheses1() {
ASSERT_EQUALS("a <= 110 ;", tok("a <= (10+100);"));
ASSERT_EQUALS("{ while ( x ( ) == -1 ) { } }", tok("{while((x()) == -1){ }}"));
}
void declareVar() {
const char code[] = "void f ( ) { char str [ 100 ] = \"100\" ; }";
ASSERT_EQUALS(code, tok(code));
@ -2334,7 +2295,7 @@ private:
{
const char code[] = "tr = (struct reg){ .a = (1), .c = (2) };";
const char expected[] = "tr = ( struct reg ) { . a = 1 , . c = 2 } ;";
const char expected[] = "tr = ( struct reg ) { . a = 1 , . c = ( 2 ) } ;";
ASSERT_EQUALS(expected, tok(code));
}
}
@ -2414,120 +2375,6 @@ private:
}
}
void calculations() {
{
const char code[] = "a[i+8+2];";
ASSERT_EQUALS("a [ i + 10 ] ;", tok(code));
}
{
const char code[] = "a[8+2+i];";
ASSERT_EQUALS("a [ 10 + i ] ;", tok(code));
}
{
const char code[] = "a[i + 2 * (2 * 4)];";
ASSERT_EQUALS("a [ i + 16 ] ;", tok(code));
}
{
const char code[] = "a[i + 100 - 90];";
ASSERT_EQUALS("a [ i + 10 ] ;", tok(code));
}
{
const char code[] = "a[1+1+1+1+1+1+1+1+1+1-2+5-3];";
ASSERT_EQUALS("a [ 10 ] ;", tok(code));
}
{
const char code[] = "a[10+10-10-10];";
ASSERT_EQUALS("a [ 0 ] ;", tok(code));
}
ASSERT_EQUALS("a [ 4 ] ;", tok("a[1+3|4];"));
ASSERT_EQUALS("a [ 4U ] ;", tok("a[1+3|4U];"));
ASSERT_EQUALS("a [ 3 ] ;", tok("a[1+2&3];"));
ASSERT_EQUALS("a [ 3U ] ;", tok("a[1+2&3U];"));
ASSERT_EQUALS("a [ 5 ] ;", tok("a[1-0^4];"));
ASSERT_EQUALS("a [ 5U ] ;", tok("a[1-0^4U];"));
ASSERT_EQUALS("x = 1 + 2 * y ;", tok("x=1+2*y;"));
ASSERT_EQUALS("x = 7 ;", tok("x=1+2*3;"));
ASSERT_EQUALS("x = 47185 ;", tok("x=(65536*72/100);"));
ASSERT_EQUALS("x = 1500000 / ( ( 90000 ) * 1000 / 54000 ) ;", tok("x = 1500000 / ((145000 - 55000) * 1000 / 54000);"));
ASSERT_EQUALS("int a [ 8 ] ;", tok("int a[5+6/2];"));
ASSERT_EQUALS("int a [ 4 ] ;", tok("int a[(10)-1-5];"));
ASSERT_EQUALS("int a [ i - 9 ] ;", tok("int a[i - 10 + 1];"));
ASSERT_EQUALS("int a [ i - 11 ] ;", tok("int a[i - 10 - 1];"));
ASSERT_EQUALS("x = y ;", tok("x=0+y+0-0;"));
ASSERT_EQUALS("x = 0 ;", tok("x=0*y;"));
ASSERT_EQUALS("x = 501 ;", tok("x = 1000 + 2 >> 1;"));
ASSERT_EQUALS("x = 125 ;", tok("x = 1000 / 2 >> 2;"));
{
// Ticket #1997
const char code[] = "void * operator new[](size_t);";
ASSERT_EQUALS("void * operatornew[] ( long ) ;", tok(code, true, Settings::Win32A));
}
ASSERT_EQUALS("; a [ 0 ] ;", tok(";a[0*(*p)];"));
ASSERT_EQUALS(";", tok("; x = x + 0;"));
ASSERT_EQUALS("{ if ( a == 2 ) { } }", tok("{if (a==1+1){}}"));
ASSERT_EQUALS("{ if ( a + 2 != 6 ) { } }", tok("{if (a+1+1!=1+2+3){}}"));
ASSERT_EQUALS("{ if ( 4 < a ) { } }", tok("{if (14-2*5<a*4/(2*2)){}}"));
ASSERT_EQUALS("( y / 2 - 2 ) ;", tok("(y / 2 - 2);"));
ASSERT_EQUALS("( y % 2 - 2 ) ;", tok("(y % 2 - 2);"));
ASSERT_EQUALS("( 4 ) ;", tok("(1 * 2 / 1 * 2);")); // #3722
ASSERT_EQUALS("x ( 60129542144 ) ;", tok("x(14<<4+17<<300%17);")); // #4931
ASSERT_EQUALS("x ( 1 ) ;", tok("x(8|5&6+0 && 7);")); // #6104
ASSERT_EQUALS("x ( 1 ) ;", tok("x(2 && 4<<4<<5 && 4);")); // #4933
ASSERT_EQUALS("x ( 1 ) ;", tok("x(9&&8%5%4/3);")); // #4931
ASSERT_EQUALS("x ( 1 ) ;", tok("x(2 && 2|5<<2%4);")); // #4931
ASSERT_EQUALS("x ( -2 << 6 | 1 ) ;", tok("x(1-3<<6|5/3);")); // #4931
ASSERT_EQUALS("x ( 2 ) ;", tok("x(2|0*0&2>>1+0%2*1);")); // #4931
ASSERT_EQUALS("x ( 0 & 4 != 1 ) ;", tok("x(4%1<<1&4!=1);")); // #4931 (can be simplified further but it's not a problem)
ASSERT_EQUALS("x ( 1 ) ;", tok("x(0&&4>0==2||4);")); // #4931
// don't remove these spaces..
ASSERT_EQUALS("new ( auto ) ( 4 ) ;", tok("new (auto)(4);"));
}
void comparisons() {
ASSERT_EQUALS("( 1 ) ;", tok("( 1 < 2 );"));
ASSERT_EQUALS("( x && 1 ) ;", tok("( x && 1 < 2 );"));
ASSERT_EQUALS("( 5 ) ;", tok("( 1 < 2 && 3 < 4 ? 5 : 6 );"));
ASSERT_EQUALS("( 6 ) ;", tok("( 1 > 2 && 3 > 4 ? 5 : 6 );"));
}
void simplifyCalculations() {
ASSERT_EQUALS("void foo ( char str [ ] ) { char x ; x = ( * str ) ; }",
tok("void foo ( char str [ ] ) { char x = 0 | ( * str ) ; }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (b + 0) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (0 + b) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (b - 0) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (b * 1) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (1 * b) { } }"));
//ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
// tok("void foo ( ) { if (b / 1) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (b | 0) { } }"));
ASSERT_EQUALS("void foo ( ) { if ( b ) { } }",
tok("void foo ( ) { if (0 | b) { } }"));
ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }",
tok("void foo ( int b ) { int a = b | 0 ; bar ( a ) ; }"));
ASSERT_EQUALS("void foo ( int b ) { int a ; a = b ; bar ( a ) ; }",
tok("void foo ( int b ) { int a = 0 | b ; bar ( a ) ; }"));
}
void simplifyOperator1() {
// #3237 - error merging namespaces with operators
const char code[] = "class c {\n"
@ -2607,16 +2454,6 @@ private:
}
}
void pointeralias2() {
const char code[] = "void f()\n"
"{\n"
" int i;\n"
" int *p = &i;\n"
" return *p;\n"
"}\n";
ASSERT_EQUALS("void f ( ) { int i ; int * p ; return i ; }", tok(code));
}
void pointeralias3() {
const char code[] = "void f()\n"
"{\n"
@ -2635,18 +2472,6 @@ private:
ASSERT_EQUALS(expected, tok(code));
}
void pointeralias4() {
const char code[] = "int f()\n"
"{\n"
" int i;\n"
" int *p = &i;\n"
" *p = 5;\n"
" return i;\n"
"}\n";
const char expected[] = "int f ( ) { int i ; int * p ; i = 5 ; return 5 ; }";
ASSERT_EQUALS(expected, tok(code));
}
void simplifyStructDecl1() {
{
const char code[] = "struct ABC { } abc;";
@ -3228,37 +3053,9 @@ private:
std::istringstream istr(code);
ASSERT_LOC(tokenizer.tokenize(istr, "test.cpp"), file, line);
(tokenizer.simplifyKnownVariables)();
return tokenizer.tokens()->stringifyList(nullptr, false);
}
void simplifyKnownVariables1() {
{
const char code[] = "void f()\n"
"{\n"
" int a = 10;\n"
" if (a);\n"
"}\n";
ASSERT_EQUALS(
"void f ( ) { int a ; a = 10 ; if ( 10 ) { ; } }",
simplifyKnownVariables(code));
}
{
const char code[] = "void f()\n"
"{\n"
" int a = 10;\n"
" if (!a);\n"
"}\n";
ASSERT_EQUALS(
"void f ( ) { int a ; a = 10 ; if ( ! 10 ) { ; } }",
simplifyKnownVariables(code));
}
}
void simplifyKnownVariables2() {
const char code[] = "void f()\n"
"{\n"
@ -3313,136 +3110,6 @@ private:
simplifyKnownVariables(code));
}
void simplifyKnownVariables6() {
const char code[] = "void f()\n"
"{\n"
" char str[2];"
" int a = 4;\n"
" str[a] = 0;\n"
"}\n";
ASSERT_EQUALS(
"void f ( ) { char str [ 2 ] ; int a ; a = 4 ; str [ 4 ] = 0 ; }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables7() {
const char code[] = "void foo()\n"
"{\n"
" int i = 22;\n"
" abc[i++] = 1;\n"
" abc[++i] = 2;\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int i ; i = 24 ; abc [ 22 ] = 1 ; abc [ 24 ] = 2 ; }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables8() {
const char code[] = "void foo()\n"
"{\n"
" int i = 22;\n"
" i++;\n"
" abc[i] = 0;\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int i ; i = 23 ; abc [ 23 ] = 0 ; }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables9() {
const char code[] = "void foo()\n"
"{\n"
" int a = 1, b = 2;\n"
" if (a < b)\n"
" ;\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int a ; a = 1 ; int b ; b = 2 ; if ( 1 < 2 ) { ; } }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables10() {
{
const char code[] = "void f()\n"
"{\n"
" bool b=false;\n"
"\n"
" {\n"
" b = true;\n"
" }\n"
"\n"
" if( b )\n"
" {\n"
" a();\n"
" }\n"
"}\n";
const std::string expected1("void f ( ) {"
" bool b ; b = false ;"
" { b = true ; }");
TODO_ASSERT_EQUALS(
expected1 + " if ( true ) { a ( ) ; } }",
expected1 + " if ( b ) { a ( ) ; } }",
simplifyKnownVariables(code));
}
{
const char code[] = "void f()\n"
"{\n"
" bool b=false;\n"
" { b = false; }\n"
" {\n"
" b = true;\n"
" }\n"
"\n"
" if( b )\n"
" {\n"
" a();\n"
" }\n"
"}\n";
TODO_ASSERT_EQUALS(
"void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( true ) { a ( ) ; } }",
"void f ( ) { bool b ; b = false ; { b = false ; } { b = true ; } if ( b ) { a ( ) ; } }",
simplifyKnownVariables(code));
}
{
const char code[] = "void f()\n"
"{\n"
" int b=0;\n"
" b = 1;\n"
" for( int i = 0; i < 10; i++ )"
" {\n"
" }\n"
"\n"
" return b;\n"
"}\n";
ASSERT_EQUALS(
"void f ( ) { int b ; b = 0 ; b = 1 ; for ( int i = 0 ; i < 10 ; i ++ ) { } return 1 ; }",
simplifyKnownVariables(code));
}
}
void simplifyKnownVariables11() {
const char code[] = "const int foo = 0;\n"
"int main()\n"
"{\n"
" int foo=0;\n"
"}\n";
ASSERT_EQUALS(
"int main ( ) { int foo ; foo = 0 ; }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables13() {
const char code[] = "void f()\n"
"{\n"
@ -3461,32 +3128,6 @@ private:
ASSERT_EQUALS(code, simplifyKnownVariables(code));
}
void simplifyKnownVariables15() {
{
const char code[] = "int main()\n"
"{\n"
" int x=5;\n"
" std::cout << 10 / x << std::endl;\n"
"}\n";
ASSERT_EQUALS(
"int main ( ) { int x ; x = 5 ; std :: cout << 10 / 5 << std :: endl ; }",
simplifyKnownVariables(code));
}
{
const char code[] = "int main()\n"
"{\n"
" int x=5;\n"
" std::cout << x / ( x == 1 ) << std::endl;\n"
"}\n";
ASSERT_EQUALS(
"int main ( ) { int x ; x = 5 ; std :: cout << 5 / ( 5 == 1 ) << std :: endl ; }",
simplifyKnownVariables(code));
}
}
void simplifyKnownVariables16() {
// ticket #807 - segmentation fault when macro isn't found
const char code[] = "void f ( ) { int n = 1; DISPATCH(while); }";
@ -3515,111 +3156,12 @@ private:
simplifyKnownVariables(code));
}
void simplifyKnownVariables20() {
const char code[] = "void f()\n"
"{\n"
" int i = 0;\n"
" if (x) {\n"
" if (i) i=0;\n"
" }\n"
"}\n";
ASSERT_EQUALS(
"void f ( ) { int i ; i = 0 ; if ( x ) { if ( 0 ) { i = 0 ; } } }",
simplifyKnownVariables(code));
}
void simplifyKnownVariables21() {
const char code[] = "void foo() { int n = 10; for (int i = 0; i < n; ++i) { } }";
ASSERT_EQUALS(
"void foo ( ) { int n ; n = 10 ; for ( int i = 0 ; i < 10 ; ++ i ) { } }",
simplifyKnownVariables(code));
ASSERT_EQUALS(
"void foo ( int i ) { int n ; n = i ; for ( i = 0 ; i < n ; ++ i ) { } }",
simplifyKnownVariables("void foo(int i) { int n = i; for (i = 0; i < n; ++i) { } }"));
}
void simplifyKnownVariables22() {
// This testcase is related to ticket #1169
{
const char code[] = "void foo()\n"
"{\n"
" int n = 10;\n"
" i = (n >> 1);\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int n ; n = 10 ; i = 10 >> 1 ; }",
simplifyKnownVariables(code));
}
{
const char code[] = "void foo()\n"
"{\n"
" int n = 10;\n"
" i = (n << 1);\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int n ; n = 10 ; i = 10 << 1 ; }",
simplifyKnownVariables(code));
}
{
const char code[] = "void foo()\n"
"{\n"
" int n = 10;\n"
" i = (1 << n);\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int n ; n = 10 ; i = 1 << 10 ; }",
simplifyKnownVariables(code));
}
{
const char code[] = "void foo()\n"
"{\n"
" int n = 10;\n"
" i = (1 >> n);\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) { int n ; n = 10 ; i = 1 >> 10 ; }",
simplifyKnownVariables(code));
}
}
void simplifyKnownVariables23() {
// This testcase is related to ticket #1596
const char code[] = "void foo(int x)\n"
"{\n"
" int a[10], c = 0;\n"
" if (x) {\n"
" a[c] = 0;\n"
" c++;\n"
" } else {\n"
" a[c] = 0;\n"
" }\n"
"}\n";
TODO_ASSERT_EQUALS(
"void foo ( int x ) "
"{"
" int a [ 10 ] ; int c ; c = 0 ;"
" if ( x ) { a [ 0 ] = 0 ; c = 1 ; }"
" else { a [ 0 ] = 0 ; } "
"}",
"void foo ( int x ) "
"{"
" int a [ 10 ] ; int c ; c = 0 ;"
" if ( x ) { a [ 0 ] = 0 ; c ++ ; }"
" else { a [ c ] = 0 ; } "
"}",
simplifyKnownVariables(code));
}
void simplifyKnownVariables25() {
{
// This testcase is related to ticket #1646
@ -3672,43 +3214,6 @@ private:
}
}
void simplifyKnownVariables27() {
// This testcase is related to ticket #1633
const char code[] = "void foo()\n"
"{\n"
" int i1 = 1;\n"
" int i2 = 2;\n"
" int i3 = (i1 + i2) * 3;\n"
"}\n";
ASSERT_EQUALS(
"void foo ( ) "
"{"
" int i1 ; i1 = 1 ;"
" int i2 ; i2 = 2 ;"
" int i3 ; i3 = ( 1 + 2 ) * 3 ; "
"}",
simplifyKnownVariables(code));
}
void simplifyKnownVariables28() {
const char code[] = "void foo(int g)\n"
"{\n"
" int i = 2;\n"
" if (g) {\n"
" }\n"
" if (i > 0) {\n"
" }\n"
"}\n";
ASSERT_EQUALS(
"void foo ( int g ) "
"{"
" int i ; i = 2 ;"
" if ( g ) { }"
" if ( 2 > 0 ) { } "
"}",
simplifyKnownVariables(code));
}
void simplifyKnownVariables29() { // ticket #1811
{
const char code[] = "int foo(int u, int v)\n"
@ -4049,48 +3554,6 @@ private:
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
void simplifyKnownVariables31() {
const char code[] = "void foo(const char str[]) {\n"
" const char *p = str;\n"
" if (p[0] == 0) {\n"
" }\n"
"}\n";
const char expected[] = "void foo ( const char str [ ] ) {\n"
"const char * p ; p = str ;\n"
"if ( str [ 0 ] == 0 ) {\n"
"}\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
void simplifyKnownVariables32() {
{
const char code[] = "void foo() {\n"
" const int x = 0;\n"
" bar(0,x);\n"
"}\n";
const char expected[] = "void foo ( ) {\n\nbar ( 0 , 0 ) ;\n}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
{
const char code[] = "static int const SZ = 22; char str[SZ];\n";
ASSERT_EQUALS("char str [ 22 ] ;", tokenizeAndStringify(code,true));
}
}
void simplifyKnownVariables33() {
const char code[] = "static void foo(struct Foo *foo) {\n"
" foo->a = 23;\n"
" x[foo->a] = 0;\n"
"}\n";
const char expected[] = "static void foo ( struct Foo * foo ) {\n"
"foo . a = 23 ;\n"
"x [ 23 ] = 0 ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
void simplifyKnownVariables34() {
const char code[] = "void f() {\n"
" int x = 10;\n"
@ -4106,13 +3569,6 @@ private:
}
void simplifyKnownVariables36() {
// Ticket #2304
const char code[] = "void f() {"
" const char *q = \"hello\";"
" strcpy(p, q);"
"}";
const char expected[] = "void f ( ) { const char * q ; q = \"hello\" ; strcpy ( p , \"hello\" ) ; }";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
// Ticket #5972
const char code2[] = "void f() {"
@ -4123,50 +3579,7 @@ private:
ASSERT_EQUALS(expected2, tokenizeAndStringify(code2, true));
}
void simplifyKnownVariables39() {
// Ticket #2296 - simplify pointer alias 'delete p;'
{
const char code[] = "void f() {\n"
" int *x;\n"
" int *y = x;\n"
" delete y;\n"
"}";
ASSERT_EQUALS("void f ( ) {\nint * x ;\nint * y ; y = x ;\ndelete x ;\n}", tokenizeAndStringify(code, true));
}
{
const char code[] = "void f() {\n"
" int *x;\n"
" int *y = x;\n"
" delete [] y;\n"
"}";
ASSERT_EQUALS("void f ( ) {\nint * x ;\nint * y ; y = x ;\ndelete [ ] x ;\n}", tokenizeAndStringify(code, true));
}
}
void simplifyKnownVariables41() {
const char code[] = "void f() {\n"
" int x = 0;\n"
" const int *p; p = &x;\n"
" if (p) { return 0; }\n"
"}";
ASSERT_EQUALS("void f ( ) {\nint x ; x = 0 ;\nconst int * p ; p = & x ;\nif ( & x ) { return 0 ; }\n}", tokenizeAndStringify(code, true));
}
void simplifyKnownVariables42() {
{
const char code[] = "void f() {\n"
" char str1[10], str2[10];\n"
" strcpy(str1, \"abc\");\n"
" strcpy(str2, str1);\n"
"}";
const char expected[] = "void f ( ) {\n"
"char str1 [ 10 ] ; char str2 [ 10 ] ;\n"
"strcpy ( str1 , \"abc\" ) ;\n"
"strcpy ( str2 , \"abc\" ) ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
{
const char code[] = "void f() {\n"
" char a[10];\n"
@ -4261,20 +3674,6 @@ private:
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
void simplifyKnownVariables45() {
const char code[] = "class Fred {\n"
"private:\n"
" const static int NUM = 2;\n"
" int array[NUM];\n"
"}";
const char expected[] = "class Fred {\n"
"private:\n"
"\n"
"int array [ 2 ] ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
void simplifyKnownVariables46() {
const char code[] = "void f() {\n"
" int x = 0;\n"
@ -4335,45 +3734,6 @@ private:
}
void simplifyKnownVariables50() { // #4066
{
const char code[] = "void f() {\n"
" char str1[10], str2[10];\n"
" sprintf(str1, \"%%\");\n"
" strcpy(str2, str1);\n"
"}";
const char expected[] = "void f ( ) {\n"
"char str1 [ 10 ] ; char str2 [ 10 ] ;\n"
"sprintf ( str1 , \"%%\" ) ;\n"
"strcpy ( str2 , \"%\" ) ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
{
const char code[] = "void f() {\n"
" char str1[25], str2[25];\n"
" sprintf(str1, \"abcdef%%%% and %% and %\");\n"
" strcpy(str2, str1);\n"
"}";
const char expected[] = "void f ( ) {\n"
"char str1 [ 25 ] ; char str2 [ 25 ] ;\n"
"sprintf ( str1 , \"abcdef%%%% and %% and %\" ) ;\n"
"strcpy ( str2 , \"abcdef%% and % and %\" ) ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
{
const char code[] = "void f() {\n"
" char str1[10], str2[10];\n"
" sprintf(str1, \"abc\");\n"
" strcpy(str2, str1);\n"
"}";
const char expected[] = "void f ( ) {\n"
"char str1 [ 10 ] ; char str2 [ 10 ] ;\n"
"sprintf ( str1 , \"abc\" ) ;\n"
"strcpy ( str2 , \"abc\" ) ;\n"
"}";
ASSERT_EQUALS(expected, tokenizeAndStringify(code, true));
}
{
//don't simplify '&x'!
const char code[] = "const char * foo ( ) {\n"
@ -4425,31 +3785,15 @@ private:
ASSERT_THROW(tokenizeAndStringify(code, true), InternalError);
}
void simplifyKnownVariables53() { // references
ASSERT_EQUALS("void f ( ) { int x ; x = abc ( ) ; }", tokenizeAndStringify("void f() { int x; int &ref=x; ref=abc(); }", true));
ASSERT_EQUALS("void f ( ) { int * p ; p = abc ( ) ; }", tokenizeAndStringify("void f() { int *p; int *&ref=p; ref=abc(); }", true));
}
void simplifyKnownVariables54() { // #4913
ASSERT_EQUALS("void f ( int * p ) { * -- p = 0 ; * p = 0 ; }", tokenizeAndStringify("void f(int*p) { *--p=0; *p=0; }", true));
}
void simplifyKnownVariables55() { // pointer alias
ASSERT_EQUALS("void f ( ) { int a ; int * p ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (*p>0) {} }", true));
ASSERT_EQUALS("void f ( ) { int a ; struct AB ab ; ab . a = & a ; if ( a > 0 ) { } }", tokenizeAndStringify("void f() { int a; struct AB ab; ab.a = &a; if (*ab.a>0) {} }", true));
ASSERT_EQUALS("void f ( ) { int a ; int * p ; if ( x > a ) { } }", tokenizeAndStringify("void f() { int a; int *p=&a; if (x>*p) {} }", true));
}
void simplifyKnownVariables56() { // ticket #5301 - >>
ASSERT_EQUALS("void f ( ) { int a ; a = 0 ; int b ; b = 0 ; * p >> a >> b ; return a / b ; }",
tokenizeAndStringify("void f() { int a=0,b=0; *p>>a>>b; return a/b; }", true));
}
void simplifyKnownVariables57() { // #4724
ASSERT_EQUALS("unsigned long long x ; x = 9223372036854775808UL ;", tokenizeAndStringify("unsigned long long x = 1UL << 63 ;", true));
ASSERT_EQUALS("long long x ; x = -9223372036854775808L ;", tokenizeAndStringify("long long x = 1L << 63 ;", true));
}
void simplifyKnownVariables58() { // #5268
const char code[] = "enum e { VAL1 = 1, VAL2 }; "
"typedef char arr_t[VAL2]; "
@ -4485,21 +3829,6 @@ private:
"}", tokenizeAndStringify(code, true));
}
void simplifyKnownVariables60() { // #6829
const char code[] = "void f() {\n"
" int i = 1;\n"
" const int * const constPtrToConst = &i;\n"
" std::cout << *constPtrToConst << std::endl;\n"
" std::cout << constPtrToConst << std::endl;\n"
"}";
ASSERT_EQUALS("void f ( ) {\n"
"int i ; i = 1 ;\n"
"const int * const constPtrToConst ; constPtrToConst = & i ;\n"
"std :: cout << i << std :: endl ;\n"
"std :: cout << & i << std :: endl ;\n"
"}", tokenizeAndStringify(code, true));
}
void simplifyKnownVariables61() { // #7805
tokenizeAndStringify("static const int XX = 0;\n"
"enum E { XX };\n"
@ -4679,22 +4008,6 @@ private:
ASSERT_EQUALS("static int x ; void f ( ) { x = 123 ; while ( ! x ) { dostuff ( ) ; } }", tokenizeAndStringify(code,true));
}
void simplifyKnownVariablesPointerAliasFunctionCall() { // #7440
const char code[] = "int main() {\n"
" char* data = new char[100];\n"
" char** dataPtr = &data;\n"
" printf(\"test\");\n"
" delete [] *dataPtr;\n"
"}";
const char exp[] = "int main ( ) {\n"
"char * data ; data = new char [ 100 ] ;\n"
"char * * dataPtr ; dataPtr = & data ;\n"
"printf ( \"test\" ) ;\n"
"delete [ ] data ;\n"
"}";
ASSERT_EQUALS(exp, tokenizeAndStringify(code, /*simplify=*/ true));
}
void simplifyKnownVariablesNamespace() {
{ // #10059
const char code[] = "namespace N {\n"
@ -4785,25 +4098,6 @@ private:
}
}
void simplify_constants2() {
const char code[] =
"void f( Foo &foo, Foo *foo2 ) {\n"
"const int a = 45;\n"
"foo.a=a+a;\n"
"foo2->a=a;\n"
"}";
ASSERT_EQUALS("void f ( Foo & foo , Foo * foo2 ) { foo . a = 90 ; foo2 . a = 45 ; }", tok(code));
}
void simplify_constants4() {
const char code[] = "static const int bSize = 4;\n"
"static const int aSize = 50;\n"
"x = bSize;\n"
"y = aSize;\n";
ASSERT_EQUALS("x = 4 ; y = 50 ;", tok(code));
}
void simplify_constants6() { // Ticket #5625
{
const char code[] = "template < class T > struct foo ;\n"

View File

@ -854,6 +854,12 @@ private:
" return d;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
checkNormal("std::string f() {\n"
" std::map<int, std::string> m = { { 1, \"1\" } };\n"
" return m.at(1);\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void outOfBoundsSymbolic()

View File

@ -490,6 +490,7 @@ private:
TEST_CASE(auto14);
TEST_CASE(auto15); // C++17 auto deduction from braced-init-list
TEST_CASE(auto16);
TEST_CASE(auto17); // #11163
TEST_CASE(unionWithConstructor);
@ -8601,6 +8602,18 @@ private:
ASSERT_EQUALS(ValueType::Type::RECORD, i->valueType()->type);
}
void auto17() { // #11163 don't hang
GET_SYMBOL_DB("void f() {\n"
" std::shared_ptr<int> s1;\n"
" auto s2 = std::shared_ptr(s1);\n"
"}\n"
"void g() {\n"
" std::shared_ptr<int> s;\n"
" auto w = std::weak_ptr(s);\n"
"}\n");
ASSERT_EQUALS(5, db->variableList().size());
}
void unionWithConstructor() {
GET_SYMBOL_DB("union Fred {\n"
" Fred(int x) : i(x) { }\n"

View File

@ -3525,7 +3525,7 @@ private:
" if (!x) i = 0;\n"
" if (!x || i>0) {}\n" // <- error
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Uninitialized variable: i\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (warning) Uninitialized variable: i\n", errout.str());
valueFlowUninit("void f(int x) {\n"
" int i;\n"
@ -3540,7 +3540,7 @@ private:
" else i = 0;\n"
" if (x || i>0) {}\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Uninitialized variable: i\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Uninitialized variable: i\n", errout.str());
valueFlowUninit("void f(int x) {\n"
" int i;\n"
@ -3573,7 +3573,7 @@ private:
" a = y;\n"
" return y ? 2*a : 3*a;\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (error) Uninitialized variable: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:5]: (warning) Uninitialized variable: a\n", errout.str());
valueFlowUninit("void f() {\n" // Don't crash
" int a;\n"
@ -3623,6 +3623,17 @@ private:
" if (a[i]) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
valueFlowUninit("void f(void) {\n"
" char *c;\n"
" char x;\n"
" while (true) {\n"
" c = &x;\n"
" break;\n"
" }\n"
" ++c;\n"
"}", "test.c");
ASSERT_EQUALS("", errout.str());
}
void uninitStructMember() { // struct members
@ -4675,7 +4686,7 @@ private:
" s.x = 42;\n"
" bar(&s);\n"
"}");
ASSERT_EQUALS("[test.cpp:18] -> [test.cpp:12] -> [test.cpp:8]: (error) Uninitialized variable: s->flag\n",
ASSERT_EQUALS("[test.cpp:18] -> [test.cpp:12] -> [test.cpp:8]: (warning) Uninitialized variable: s->flag\n",
errout.str());
// Ticket #2207 - False negative
@ -4820,7 +4831,7 @@ private:
" p = new S(io);\n"
" p->Write();\n"
"}");
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:10]: (error) Uninitialized variable: p.rIo\n", errout.str());
ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:10]: (warning) Uninitialized variable: p.rIo\n", errout.str());
// Unknown types
{
@ -5140,7 +5151,7 @@ private:
" }\n"
" printf(\"\", value);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:9]: (error) Uninitialized variable: value\n", errout.str());
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:9]: (warning) Uninitialized variable: value\n", errout.str());
valueFlowUninit("void f(int x)\n"
"{\n"
@ -5242,7 +5253,7 @@ private:
" else\n"
" return -1;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (error) Uninitialized variable: a\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:6]: (warning) Uninitialized variable: a\n", errout.str());
// #9772
valueFlowUninit("int func(void) {\n"
@ -5366,7 +5377,7 @@ private:
" increment(n);\n"
" return n;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (error) Uninitialized variable: i\n", errout.str());
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:1]: (warning) Uninitialized variable: i\n", errout.str());
}
void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value
@ -5418,7 +5429,7 @@ private:
" someType_t gVar;\n"
" bar(&gVar);\n"
"}");
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5]: (error) Uninitialized variable: p->flags\n", errout.str());
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:5]: (warning) Uninitialized variable: p->flags\n", errout.str());
valueFlowUninit("typedef struct\n"
"{\n"
@ -5619,7 +5630,7 @@ private:
" bool copied_all = true;\n"
" g(&copied_all, 5, 6, &bytesCopied);\n"
"}");
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (error) Uninitialized variable: *buflen\n", errout.str());
ASSERT_EQUALS("[test.cpp:7] -> [test.cpp:2]: (warning) Uninitialized variable: *buflen\n", errout.str());
// # 9953
valueFlowUninit("uint32_t f(uint8_t *mem) {\n"
@ -5657,7 +5668,7 @@ private:
" ab.a = 0;\n"
" do_something(ab);\n"
"}");
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (error) Uninitialized variable: ab.b\n", errout.str());
ASSERT_EQUALS("[test.cpp:6] -> [test.cpp:2]: (warning) Uninitialized variable: ab.b\n", errout.str());
valueFlowUninit("struct AB { int a; int b; };\n"
"void f(void) {\n"
@ -6187,8 +6198,17 @@ private:
"}\n");
TODO_ASSERT_EQUALS("[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->b\n"
"[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->c\n",
"[test.cpp:8] -> [test.cpp:3]: (error) Uninitialized variable: abc->b\n",
"[test.cpp:8] -> [test.cpp:3]: (warning) Uninitialized variable: abc->b\n",
errout.str());
valueFlowUninit("struct S { int* p; };\n" // #10463
"void f(S* in) {\n"
" S* s;\n"
" memcpy(in, s, sizeof(S));\n"
" s->p = NULL;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: s\n",
errout.str());
}
void uninitvar_memberfunction() {

View File

@ -315,6 +315,8 @@ int main(int argc, char **argv)
<< " endif # !CPPCHK_GLIBCXX_DEBUG\n"
<< " endif # GNU/kFreeBSD\n"
<< "\n"
<< " LDFLAGS=-pthread\n"
<< "\n"
<< "endif # WINNT\n"
<< "\n";
@ -413,7 +415,7 @@ int main(int argc, char **argv)
fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n";
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
fout << "all:\tcppcheck testrunner\n\n";
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n";
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n";
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
fout << "test:\tall\n";
fout << "\t./testrunner\n\n";