diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 2f7dd0de492..e9e6e09181d 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -303,6 +303,12 @@ jobs: make -j$(nproc) ./test-projectfile popd + pushd gui/test/resultstree + qmake CONFIG+=debug CONFIG+=ccache + make -j$(nproc) + export QT_QPA_PLATFORM=offscreen + ./test-resultstree + popd pushd gui/test/translationhandler qmake CONFIG+=debug CONFIG+=ccache make -j$(nproc) diff --git a/.github/workflows/scriptcheck.yml b/.github/workflows/scriptcheck.yml index 5f702f2fb22..5d428211265 100644 --- a/.github/workflows/scriptcheck.yml +++ b/.github/workflows/scriptcheck.yml @@ -48,7 +48,7 @@ jobs: runs-on: ubuntu-20.04 strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] + python-version: [3.6, 3.7, 3.8, 3.9, '3.10', '3.11', '3.12'] include: - python-version: '3.12' python-latest: true diff --git a/Makefile b/Makefile index c97ea02ffdb..0d3f88f0cbb 100644 --- a/Makefile +++ b/Makefile @@ -858,7 +858,7 @@ test/testtoken.o: test/testtoken.cpp lib/addoninfo.h lib/check.h lib/color.h lib test/testtokenize.o: test/testtokenize.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenize.cpp -test/testtokenlist.o: test/testtokenlist.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h +test/testtokenlist.o: test/testtokenlist.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testtokenlist.cpp test/testtokenrange.o: test/testtokenrange.cpp lib/addoninfo.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/sourcelocation.h lib/standards.h lib/suppressions.h lib/symboldatabase.h lib/templatesimplifier.h lib/token.h lib/tokenize.h lib/tokenlist.h lib/tokenrange.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h diff --git a/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index c1232a95cb2..ab20f5848b0 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1638,6 +1638,7 @@ void CmdLineParser::printHelp() const " * misra-c-2012 Misra C 2012\n" " * misra-c-2023 Misra C 2023\n" " * misra-c++-2008 Misra C++ 2008\n" + " * misra-c++-2023 Misra C++ 2023\n" " Other:\n" " * bughunting Soundy analysis\n" " * cert-c-int-precision=BITS Integer precision to use in Cert C analysis.\n" diff --git a/cli/main.cpp b/cli/main.cpp index 618bcbac4ab..4cabb0537d3 100644 --- a/cli/main.cpp +++ b/cli/main.cpp @@ -20,7 +20,7 @@ /** * * @mainpage Cppcheck - * @version 2.14.0 + * @version 2.14.1 * * @section overview_sec Overview * Cppcheck is a simple tool for static analysis of C/C++ code. diff --git a/cmake/options.cmake b/cmake/options.cmake index d63d67079af..a18592f588a 100644 --- a/cmake/options.cmake +++ b/cmake/options.cmake @@ -22,19 +22,23 @@ option(WARNINGS_ARE_ERRORS "Treat warnings as errors" option(EXTERNALS_AS_SYSTEM "Treat externals as system includes" OFF) set(USE_MATCHCOMPILER "Auto" CACHE STRING "Usage of match compiler") -set(_MATCHCOMPILER_STRINGS Auto Off On Verify) -set_property(CACHE USE_MATCHCOMPILER PROPERTY STRINGS ${_MATCHCOMPILER_STRINGS}) -if (NOT ${USE_MATCHCOMPILER} IN_LIST _MATCHCOMPILER_STRINGS) - message(FATAL_ERROR "Invalid USE_MATCHCOMPILER value '${USE_MATCHCOMPILER}'") -endif() -if (USE_MATCHCOMPILER STREQUAL "Auto") - if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") - set(USE_MATCHCOMPILER_OPT "On") +set_property(CACHE USE_MATCHCOMPILER PROPERTY STRINGS Auto Off On Verify) +if(USE_MATCHCOMPILER) + if(USE_MATCHCOMPILER STREQUAL "Auto") + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + message(STATUS "Non-debug build detected - enabling matchcompiler") + set(USE_MATCHCOMPILER_OPT "On") + else() + message(STATUS "Debug build detected - disabling matchcompiler") + set(USE_MATCHCOMPILER_OPT "Off") + endif() + elseif(USE_MATCHCOMPILER STREQUAL "Verify") + set(USE_MATCHCOMPILER_OPT "Verify") else() - set(USE_MATCHCOMPILER_OPT "Off") + set(USE_MATCHCOMPILER_OPT "On") endif() else() - set(USE_MATCHCOMPILER_OPT ${USE_MATCHCOMPILER}) + set(USE_MATCHCOMPILER_OPT "Off") endif() option(BUILD_CORE_DLL "Build lib as cppcheck-core.dll with Visual Studio" OFF) diff --git a/externals/simplecpp/simplecpp.cpp b/externals/simplecpp/simplecpp.cpp old mode 100644 new mode 100755 index d3bd1c0c9a2..812600bfbd0 --- a/externals/simplecpp/simplecpp.cpp +++ b/externals/simplecpp/simplecpp.cpp @@ -20,13 +20,13 @@ #include #include #include -#include +#include // IWYU pragma: keep #include #include #include #include #include -#include +#include // IWYU pragma: keep #include #include #include @@ -1984,7 +1984,7 @@ namespace simplecpp { if (paren == 0) return tok->next->next; tok = tok->next; - if (parametertokens.front()->next->str() != ")" && parametertokens.size() > args.size()) + if (parametertokens.size() > args.size() && parametertokens.front()->next->str() != ")") tok = expandToken(output, loc, tok, macros, expandedmacros, parametertokens)->previous; } } @@ -3085,9 +3085,11 @@ static std::string getFileName(const std::map::const_iterator it = dui.includePaths.begin(); it != dui.includePaths.end(); ++it) { std::string s = simplecpp::simplifyPath(getIncludePathFileName(*it, header)); @@ -3143,6 +3145,8 @@ std::map simplecpp::load(const simplecpp::To continue; } + if (dui.removeComments) + tokenlist->removeComments(); ret[filename] = tokenlist; filelist.push_back(tokenlist->front()); } @@ -3178,6 +3182,8 @@ std::map simplecpp::load(const simplecpp::To f.close(); TokenList *tokens = new TokenList(header2, filenames, outputList); + if (dui.removeComments) + tokens->removeComments(); ret[header2] = tokens; if (tokens->front()) filelist.push_back(tokens->front()); @@ -3268,6 +3274,9 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL sizeOfType.insert(std::make_pair("double *", sizeof(double *))); sizeOfType.insert(std::make_pair("long double *", sizeof(long double *))); + // use a dummy vector for the macros because as this is not part of the file and would add an empty entry - e.g. /usr/include/poll.h + std::vector dummy; + const bool hasInclude = (dui.std.size() == 5 && dui.std.compare(0,3,"c++") == 0 && dui.std >= "c++17"); MacroMap macros; for (std::list::const_iterator it = dui.defines.begin(); it != dui.defines.end(); ++it) { @@ -3279,26 +3288,26 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL continue; const std::string lhs(macrostr.substr(0,eq)); const std::string rhs(eq==std::string::npos ? std::string("1") : macrostr.substr(eq+1)); - const Macro macro(lhs, rhs, files); + const Macro macro(lhs, rhs, dummy); macros.insert(std::pair(macro.name(), macro)); } - macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", files))); - macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", files))); - macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", files))); + macros.insert(std::make_pair("__FILE__", Macro("__FILE__", "__FILE__", dummy))); + macros.insert(std::make_pair("__LINE__", Macro("__LINE__", "__LINE__", dummy))); + macros.insert(std::make_pair("__COUNTER__", Macro("__COUNTER__", "__COUNTER__", dummy))); struct tm ltime = {}; getLocaltime(ltime); - macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(<ime), files))); - macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(<ime), files))); + macros.insert(std::make_pair("__DATE__", Macro("__DATE__", getDateDefine(<ime), dummy))); + macros.insert(std::make_pair("__TIME__", Macro("__TIME__", getTimeDefine(<ime), dummy))); if (!dui.std.empty()) { std::string std_def = simplecpp::getCStdString(dui.std); if (!std_def.empty()) { - macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, files))); + macros.insert(std::make_pair("__STDC_VERSION__", Macro("__STDC_VERSION__", std_def, dummy))); } else { std_def = simplecpp::getCppStdString(dui.std); if (!std_def.empty()) - macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, files))); + macros.insert(std::make_pair("__cplusplus", Macro("__cplusplus", std_def, dummy))); } } @@ -3446,6 +3455,8 @@ void simplecpp::preprocess(simplecpp::TokenList &output, const simplecpp::TokenL header2 = openHeader(f, dui, rawtok->location.file(), header, systemheader); if (f.is_open()) { TokenList * const tokens = new TokenList(f, files, header2, outputList); + if (dui.removeComments) + tokens->removeComments(); filedata[header2] = tokens; } } diff --git a/externals/simplecpp/simplecpp.h b/externals/simplecpp/simplecpp.h old mode 100644 new mode 100755 index 7ef0740c318..c72e4c655cf --- a/externals/simplecpp/simplecpp.h +++ b/externals/simplecpp/simplecpp.h @@ -272,6 +272,10 @@ namespace simplecpp { /** sizeof(T) */ std::map sizeOfType; + const std::vector& getFiles() const { + return files; + } + private: void combineOperators(); @@ -320,13 +324,14 @@ namespace simplecpp { * On the command line these are configured by -D, -U, -I, --include, -std */ struct SIMPLECPP_LIB DUI { - DUI() : clearIncludeCache(false) {} + DUI() : clearIncludeCache(false), removeComments(false) {} std::list defines; std::set undefined; std::list includePaths; std::list includes; std::string std; bool clearIncludeCache; + bool removeComments; /** remove comment tokens from included files */ }; SIMPLECPP_LIB long long characterLiteralToLL(const std::string& str); diff --git a/gui/compliancereportdialog.cpp b/gui/compliancereportdialog.cpp index ff3812f352f..bcdf0699fb4 100644 --- a/gui/compliancereportdialog.cpp +++ b/gui/compliancereportdialog.cpp @@ -86,11 +86,12 @@ static std::vector toStdStringList(const QStringList& from) { return ret; } -ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile) : - QDialog(nullptr), +ComplianceReportDialog::ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile, QString checkersReport) + : QDialog(nullptr), mUI(new Ui::ComplianceReportDialog), mProjectFile(projectFile), - mResultsFile(std::move(resultsFile)) + mResultsFile(std::move(resultsFile)), + mCheckersReport(std::move(checkersReport)) { mUI->setupUi(this); mUI->mEditProjectName->setText(projectFile->getProjectName()); @@ -146,6 +147,13 @@ void ComplianceReportDialog::save() mProjectFile->write(); } + QTemporaryFile tempCheckersReport; + if (tempCheckersReport.open()) { + QTextStream out(&tempCheckersReport); + out << mCheckersReport << "\n"; + tempCheckersReport.close(); + } + QTemporaryFile tempFiles; if (files && tempFiles.open()) { QTextStream out(&tempFiles); @@ -205,7 +213,8 @@ void ComplianceReportDialog::save() QStringList args{"--project-name=" + projectName, "--project-version=" + projectVersion, - "--output-file=" + outFile}; + "--output-file=" + outFile, + "--checkers-report=" + tempCheckersReport.fileName()}; if (!suppressions.isEmpty()) args << "--suppressions=" + suppressions.join(","); diff --git a/gui/compliancereportdialog.h b/gui/compliancereportdialog.h index ad3b79c5c45..af4146cef9f 100644 --- a/gui/compliancereportdialog.h +++ b/gui/compliancereportdialog.h @@ -35,7 +35,7 @@ class ComplianceReportDialog final : public QDialog Q_OBJECT public: - explicit ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile); + explicit ComplianceReportDialog(ProjectFile* projectFile, QString resultsFile, QString checkersReport); ~ComplianceReportDialog() final; private slots: @@ -46,7 +46,8 @@ private slots: Ui::ComplianceReportDialog *mUI; ProjectFile* mProjectFile; - QString mResultsFile; + const QString mResultsFile; + const QString mCheckersReport; }; #endif // COMPLIANCEREPORTDIALOG_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index b5f8713ff0a..22386db4df9 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -22,6 +22,7 @@ #include "applicationlist.h" #include "aboutdialog.h" #include "analyzerinfo.h" +#include "checkstatistics.h" #include "checkthread.h" #include "common.h" #include "cppcheck.h" @@ -1593,7 +1594,7 @@ void MainWindow::complianceReport() mUI->mResults->save(tempResults.fileName(), Report::XMLV2, mCppcheckCfgProductName); - ComplianceReportDialog dlg(mProjectFile, tempResults.fileName()); + ComplianceReportDialog dlg(mProjectFile, tempResults.fileName(), mUI->mResults->getStatistics()->getCheckersReport()); dlg.exec(); } diff --git a/gui/projectfile.ui b/gui/projectfile.ui index 9c1cd7096a4..7bc0ef3a9ae 100644 --- a/gui/projectfile.ui +++ b/gui/projectfile.ui @@ -869,11 +869,42 @@ - - - Misra C++ 2008 - - + + + + + Misra C++ + + + + + + + + 2008 + + + + + 2023 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + diff --git a/gui/projectfiledialog.cpp b/gui/projectfiledialog.cpp index 1d89c899505..2bb43eb72a4 100644 --- a/gui/projectfiledialog.cpp +++ b/gui/projectfiledialog.cpp @@ -60,6 +60,7 @@ static constexpr char ADDON_MISRA[] = "misra"; static constexpr char CODING_STANDARD_MISRA_C_2023[] = "misra-c-2023"; static constexpr char CODING_STANDARD_MISRA_CPP_2008[] = "misra-cpp-2008"; +static constexpr char CODING_STANDARD_MISRA_CPP_2023[] = "misra-cpp-2023"; static constexpr char CODING_STANDARD_CERT_C[] = "cert-c-2016"; static constexpr char CODING_STANDARD_CERT_CPP[] = "cert-cpp-2016"; static constexpr char CODING_STANDARD_AUTOSAR[] = "autosar"; @@ -391,9 +392,15 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) mUI->mBtnBrowseMisraFile->setEnabled(false); } + mUI->mMisraCpp->setEnabled(mPremium); + mUI->mMisraCppVersion->setEnabled(mUI->mMisraCpp->isChecked()); + connect(mUI->mMisraCpp, &QCheckBox::toggled, mUI->mMisraCppVersion, &QComboBox::setEnabled); + + mUI->mMisraCppVersion->setVisible(mPremium); + mUI->mMisraCppVersion->setCurrentIndex(projectFile->getCodingStandards().contains(CODING_STANDARD_MISRA_CPP_2023)); + mUI->mCertC2016->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_CERT_C)); mUI->mCertCpp2016->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_CERT_CPP)); - mUI->mMisraCpp2008->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_MISRA_CPP_2008)); mUI->mAutosar->setChecked(mPremium && projectFile->getCodingStandards().contains(CODING_STANDARD_AUTOSAR)); if (projectFile->getCertIntPrecision() <= 0) @@ -401,7 +408,6 @@ void ProjectFileDialog::loadFromProjectFile(const ProjectFile *projectFile) else mUI->mEditCertIntPrecision->setText(QString::number(projectFile->getCertIntPrecision())); - mUI->mMisraCpp2008->setEnabled(mPremium); mUI->mCertC2016->setEnabled(mPremium); mUI->mCertCpp2016->setEnabled(mPremium); mUI->mAutosar->setEnabled(mPremium); @@ -482,8 +488,10 @@ void ProjectFileDialog::saveToProjectFile(ProjectFile *projectFile) const codingStandards << CODING_STANDARD_CERT_CPP; if (mPremium && mUI->mMisraVersion->currentIndex() == 1) codingStandards << CODING_STANDARD_MISRA_C_2023; - if (mUI->mMisraCpp2008->isChecked()) + if (mUI->mMisraCpp->isChecked() && mUI->mMisraCppVersion->currentIndex() == 0) codingStandards << CODING_STANDARD_MISRA_CPP_2008; + if (mUI->mMisraCpp->isChecked() && mUI->mMisraCppVersion->currentIndex() == 1) + codingStandards << CODING_STANDARD_MISRA_CPP_2023; if (mUI->mAutosar->isChecked()) codingStandards << CODING_STANDARD_AUTOSAR; projectFile->setCodingStandards(std::move(codingStandards)); diff --git a/gui/resultstree.cpp b/gui/resultstree.cpp index 2ee74f67e44..cc80f1099f2 100644 --- a/gui/resultstree.cpp +++ b/gui/resultstree.cpp @@ -250,9 +250,16 @@ bool ResultsTree::addErrorItem(const ErrorItem &item) } // Partially refresh the tree: Unhide file item if necessary - if (!hide) { - setRowHidden(fileItem->row(), QModelIndex(), !mShowSeverities.isShown(item.severity)); + setRowHidden(stditem->row(), fileItem->index(), hide || !mShowSeverities.isShown(item.severity)); + + bool hideFile = true; + for (int i = 0; i < fileItem->rowCount(); ++i) { + if (!isRowHidden(i, fileItem->index())) { + hideFile = false; + } } + setRowHidden(fileItem->row(), QModelIndex(), hideFile); + return true; } diff --git a/gui/test/CMakeLists.txt b/gui/test/CMakeLists.txt index ea868eb97e6..d086e890cb1 100644 --- a/gui/test/CMakeLists.txt +++ b/gui/test/CMakeLists.txt @@ -1,6 +1,8 @@ add_subdirectory(cppchecklibrarydata) add_subdirectory(filelist) add_subdirectory(projectfile) +# FIXME does not work in CI +#add_subdirectory(resultstree) add_subdirectory(translationhandler) add_subdirectory(xmlreportv2) # TODO: add all tests to CTest \ No newline at end of file diff --git a/gui/test/resultstree/CMakeLists.txt b/gui/test/resultstree/CMakeLists.txt new file mode 100644 index 00000000000..9c98e8dcf35 --- /dev/null +++ b/gui/test/resultstree/CMakeLists.txt @@ -0,0 +1,24 @@ +qt_wrap_cpp(test-resultstree_SRC testresultstree.h ${CMAKE_SOURCE_DIR}/gui/resultstree.h) +add_custom_target(build-resultstree-deps SOURCES ${test-resultstree_SRC}) +add_dependencies(gui-build-deps build-resultstree-deps) +add_executable(test-resultstree + ${test-resultstree_SRC} + testresultstree.cpp + ${CMAKE_SOURCE_DIR}/gui/resultstree.cpp + ${CMAKE_SOURCE_DIR}/gui/erroritem.cpp + ${CMAKE_SOURCE_DIR}/gui/showtypes.cpp + ${CMAKE_SOURCE_DIR}/gui/report.cpp + ${CMAKE_SOURCE_DIR}/gui/xmlreportv2.cpp + ) +target_include_directories(test-resultstree PRIVATE ${CMAKE_SOURCE_DIR}/gui ${CMAKE_SOURCE_DIR}/lib) +target_compile_definitions(test-resultstree PRIVATE SRCDIR="${CMAKE_CURRENT_SOURCE_DIR}") +target_link_libraries(test-resultstree ${QT_CORE_LIB} ${QT_GUI_LIB} ${QT_WIDGETS_LIB} ${QT_TEST_LIB}) + +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") + # Q_UNUSED() in generated code + target_compile_options_safe(test-resultstree -Wno-extra-semi-stmt) +endif() + +if (REGISTER_GUI_TESTS) + add_test(NAME test-resultstree COMMAND $) +endif() diff --git a/gui/test/resultstree/resultstree.pro b/gui/test/resultstree/resultstree.pro new file mode 100644 index 00000000000..d4677d13eea --- /dev/null +++ b/gui/test/resultstree/resultstree.pro @@ -0,0 +1,28 @@ +TEMPLATE = app +TARGET = test-resultstree +DEPENDPATH += . +INCLUDEPATH += . ../../../lib +OBJECTS_DIR = ../../temp +MOC_DIR = ../../temp + +QT += widgets core +QT += testlib + +include(../common.pri) + +DEFINES += SRCDIR=\\\"$$PWD\\\" + +# tests +SOURCES += testresultstree.cpp \ + ../../resultstree.cpp \ + ../../erroritem.cpp \ + ../../showtypes.cpp \ + ../../report.cpp \ + ../../xmlreportv2.cpp + +HEADERS += testresultstree.h \ + ../../resultstree.h \ + ../../erroritem.h \ + ../../showtypes.h \ + ../../report.h \ + ../../xmlreportv2.h diff --git a/gui/test/resultstree/testresultstree.cpp b/gui/test/resultstree/testresultstree.cpp new file mode 100644 index 00000000000..55652dfbd15 --- /dev/null +++ b/gui/test/resultstree/testresultstree.cpp @@ -0,0 +1,111 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "testresultstree.h" +#include "resultstree.h" + +// headers that declare mocked functions/variables +#include "applicationlist.h" +#include "common.h" +#include "threadhandler.h" +#include "projectfile.h" +#include "xmlreportv2.h" + +#include "cppcheck.h" +#include "errorlogger.h" +#include "path.h" +#include "settings.h" + +#include + +// Mock GUI... +ProjectFile *ProjectFile::mActiveProject; +void ProjectFile::addSuppression(const SuppressionList::Suppression & /*unused*/) {} +QString ProjectFile::getWarningTags(std::size_t /*unused*/) const { + return QString(); +} +void ProjectFile::setWarningTags(std::size_t /*unused*/, const QString& /*unused*/) {} +bool ProjectFile::write(const QString & /*unused*/) { + return true; +} +std::string severityToString(Severity severity) { + return std::to_string((int)severity); +} +int ApplicationList::getApplicationCount() const { + return 0; +} +bool ThreadHandler::isChecking() const { + return false; +} +Application& ApplicationList::getApplication(const int /*unused*/) { + throw 1; +} +const Application& ApplicationList::getApplication(const int index) const { + return mApplications.at(index); +} +QString getPath(const QString &type) { + return "/" + type; +} +void setPath(const QString & /*unused*/, const QString & /*unused*/) {} +QString XmlReport::quoteMessage(const QString &message) { + return message; +} +QString XmlReport::unquoteMessage(const QString &message) { + return message; +} +XmlReport::XmlReport(const QString& filename) : Report(filename) {} + +// Mock LIB... +bool Path::isHeader(std::string const& /*unused*/) { + return false; +} +bool Path::isHeader2(std::string const& /*unused*/) { + return false; +} +const std::set ErrorLogger::mCriticalErrorIds; +std::string ErrorMessage::FileLocation::getfile(bool /*unused*/) const { + return std::string(); +} +const char* CppCheck::version() { + return "1.0"; +} +std::pair Settings::getNameAndVersion(const std::string& /*unused*/) { + throw 1; +} +Severity severityFromString(const std::string& severity) { + return (Severity)std::stoi(severity); +} + +// Test... + +void TestResultsTree::test1() const +{ + // #12772 : GUI: information messages are shown even though information tool button is deselected + ResultsTree tree(nullptr); + tree.showResults(ShowTypes::ShowType::ShowInformation, false); + ErrorItem errorItem; + errorItem.errorPath << QErrorPathItem(); + errorItem.severity = Severity::information; + tree.addErrorItem(errorItem); + QCOMPARE(tree.isRowHidden(0,QModelIndex()), true); // Added item is hidden + tree.showResults(ShowTypes::ShowType::ShowInformation, true); + QCOMPARE(tree.isRowHidden(0,QModelIndex()), false); // Show item +} + +QTEST_MAIN(TestResultsTree) + diff --git a/gui/test/resultstree/testresultstree.h b/gui/test/resultstree/testresultstree.h new file mode 100644 index 00000000000..acea181ee5b --- /dev/null +++ b/gui/test/resultstree/testresultstree.h @@ -0,0 +1,26 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2021 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include + +class TestResultsTree : public QObject { + Q_OBJECT + +private slots: + void test1() const; +}; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 86a3970d8ff..7a884164761 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,7 +10,7 @@ function(build_src output filename) get_filename_component(file ${filename} NAME) set(outfile ${CMAKE_CURRENT_BINARY_DIR}/build/mc_${file}) set(${output} ${${output}} ${outfile} PARENT_SCOPE) - if (USE_MATCHCOMPILER STREQUAL "Verify") + if (USE_MATCHCOMPILER_OPT STREQUAL "Verify") set(verify_option "--verify") endif() add_custom_command( diff --git a/lib/checkbool.cpp b/lib/checkbool.cpp index 1cb44e4744e..966cc81265a 100644 --- a/lib/checkbool.cpp +++ b/lib/checkbool.cpp @@ -341,7 +341,7 @@ void CheckBool::assignBoolToPointerError(const Token *tok) //----------------------------------------------------------------------------- void CheckBool::checkComparisonOfBoolExpressionWithInt() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("compareBoolExpressionWithInt")) return; logChecker("CheckBool::checkComparisonOfBoolExpressionWithInt"); // warning diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 01aee5b29e9..647e936c36a 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -1714,7 +1714,7 @@ void CheckClass::operatorEqMissingReturnStatementError(const Token *tok, bool er void CheckClass::operatorEqToSelf() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("operatorEqToSelf")) return; logChecker("CheckClass::operatorEqToSelf"); // warning @@ -2953,7 +2953,7 @@ void CheckClass::pureVirtualFunctionCallInConstructorError( void CheckClass::checkDuplInheritedMembers() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("duplInheritedMember")) return; logChecker("CheckClass::checkDuplInheritedMembers"); // warning diff --git a/lib/checkcondition.cpp b/lib/checkcondition.cpp index fa30ae0fcc4..39d8ad516ad 100644 --- a/lib/checkcondition.cpp +++ b/lib/checkcondition.cpp @@ -613,7 +613,9 @@ static bool isNonConstFunctionCall(const Token *ftok, const Library &library) void CheckCondition::multiCondition2() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && + !mSettings->isPremiumEnabled("identicalConditionAfterEarlyExit") && + !mSettings->isPremiumEnabled("identicalInnerCondition")) return; logChecker("CheckCondition::multiCondition2"); // warning @@ -1482,7 +1484,8 @@ void CheckCondition::alwaysTrueFalse() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("alwaysTrue") && - !mSettings->isPremiumEnabled("alwaysFalse")) + !mSettings->isPremiumEnabled("alwaysFalse") && + !mSettings->isPremiumEnabled("knownConditionTrueFalse")) return; logChecker("CheckCondition::alwaysTrueFalse"); // style diff --git a/lib/checkers.cpp b/lib/checkers.cpp index d6f6be3b861..e54943302b7 100644 --- a/lib/checkers.cpp +++ b/lib/checkers.cpp @@ -94,6 +94,7 @@ namespace checkers { {"CheckOther::checkUnreachableCode","style"}, {"CheckOther::checkVariableScope","style,notclang"}, {"CheckOther::checkPassByReference","performance,c++"}, + {"CheckOther::checkConstVariable","style,c++"}, {"CheckOther::checkConstPointer","style"}, {"CheckOther::checkCharVariable","warning,portability"}, {"CheckOther::checkIncompleteStatement","warning"}, @@ -146,6 +147,7 @@ namespace checkers { {"CheckUnusedVar::checkStructMemberUsage","style"}, {"CheckIO::checkCoutCerrMisusage","c"}, {"CheckIO::checkFileUsage",""}, + {"CheckIO::invalidScanf",""}, {"CheckIO::checkWrongPrintfScanfArguments",""}, {"CheckCondition::assignIf","style"}, {"CheckCondition::checkBadBitmaskCheck","style"}, @@ -202,6 +204,7 @@ namespace checkers { {"CheckMemoryLeakInClass::check",""}, {"CheckMemoryLeakStructMember::check",""}, {"CheckMemoryLeakNoVar::check",""}, + {"CheckMemoryLeakNoVar::checkForUnsafeArgAlloc",""}, }; const std::map premiumCheckers{ @@ -230,7 +233,6 @@ namespace checkers { {"Autosar: A2-13-1",""}, {"Autosar: A2-13-3",""}, {"Autosar: A2-13-5",""}, - {"Autosar: A2-13-6",""}, {"Autosar: A2-5-2",""}, {"Autosar: A20-8-2","warning"}, {"Autosar: A20-8-3","warning"}, @@ -404,6 +406,9 @@ namespace checkers { {"Cert C: STR32-C",""}, {"Cert C: STR34-C",""}, {"Cert C: STR38-C",""}, + {"Misra C++ 2008: 3-2-3",""}, + {"Misra C++ 2008: 3-2-4",""}, + {"Misra C++ 2008: 7-5-4",""}, {"Misra C++ 2008: M0-1-11",""}, {"Misra C++ 2008: M0-1-12",""}, {"Misra C++ 2008: M0-1-4",""}, @@ -578,18 +583,28 @@ namespace checkers { {"Misra C++ 2008: M9-6-2",""}, {"Misra C++ 2008: M9-6-3",""}, {"Misra C++ 2008: M9-6-4",""}, + {"Misra C++ 2023: 0.1.2",""}, {"Misra C++ 2023: 0.2.1",""}, + {"Misra C++ 2023: 0.2.2",""}, + {"Misra C++ 2023: 0.2.3",""}, + {"Misra C++ 2023: 0.2.4",""}, {"Misra C++ 2023: 10.0.1",""}, {"Misra C++ 2023: 10.1.2",""}, {"Misra C++ 2023: 10.2.1",""}, {"Misra C++ 2023: 10.2.2",""}, {"Misra C++ 2023: 10.2.3",""}, + {"Misra C++ 2023: 10.3.1",""}, {"Misra C++ 2023: 10.4.1",""}, {"Misra C++ 2023: 11.3.1",""}, + {"Misra C++ 2023: 11.3.2",""}, {"Misra C++ 2023: 11.6.1",""}, {"Misra C++ 2023: 11.6.3",""}, {"Misra C++ 2023: 12.2.1",""}, + {"Misra C++ 2023: 12.2.2",""}, + {"Misra C++ 2023: 12.2.3",""}, + {"Misra C++ 2023: 12.3.1",""}, {"Misra C++ 2023: 13.1.1",""}, + {"Misra C++ 2023: 13.1.2",""}, {"Misra C++ 2023: 13.3.1",""}, {"Misra C++ 2023: 13.3.2",""}, {"Misra C++ 2023: 13.3.3",""}, @@ -597,36 +612,86 @@ namespace checkers { {"Misra C++ 2023: 14.1.1",""}, {"Misra C++ 2023: 15.0.1",""}, {"Misra C++ 2023: 15.0.2",""}, + {"Misra C++ 2023: 15.1.2",""}, {"Misra C++ 2023: 15.1.3",""}, {"Misra C++ 2023: 15.1.5",""}, + {"Misra C++ 2023: 16.5.1",""}, + {"Misra C++ 2023: 16.5.2",""}, + {"Misra C++ 2023: 16.6.1",""}, {"Misra C++ 2023: 17.8.1",""}, + {"Misra C++ 2023: 18.1.1",""}, + {"Misra C++ 2023: 18.1.2",""}, + {"Misra C++ 2023: 18.3.1",""}, + {"Misra C++ 2023: 18.3.2",""}, + {"Misra C++ 2023: 18.3.3",""}, {"Misra C++ 2023: 18.4.1",""}, + {"Misra C++ 2023: 18.5.1",""}, {"Misra C++ 2023: 18.5.2",""}, + {"Misra C++ 2023: 19.0.1",""}, + {"Misra C++ 2023: 19.0.2",""}, + {"Misra C++ 2023: 19.0.3",""}, + {"Misra C++ 2023: 19.0.4",""}, + {"Misra C++ 2023: 19.1.1",""}, + {"Misra C++ 2023: 19.1.2",""}, + {"Misra C++ 2023: 19.1.3",""}, + {"Misra C++ 2023: 19.2.1",""}, + {"Misra C++ 2023: 19.2.2",""}, + {"Misra C++ 2023: 19.2.3",""}, + {"Misra C++ 2023: 19.3.1",""}, {"Misra C++ 2023: 19.3.2",""}, {"Misra C++ 2023: 19.3.3",""}, - {"Misra C++ 2023: 21.20.1",""}, - {"Misra C++ 2023: 21.20.2",""}, + {"Misra C++ 2023: 19.3.4",""}, + {"Misra C++ 2023: 19.6.1",""}, + {"Misra C++ 2023: 21.10.1",""}, + {"Misra C++ 2023: 21.10.2",""}, + {"Misra C++ 2023: 21.10.3",""}, + {"Misra C++ 2023: 21.2.1",""}, + {"Misra C++ 2023: 21.2.2",""}, + {"Misra C++ 2023: 21.2.3",""}, + {"Misra C++ 2023: 21.2.4",""}, + {"Misra C++ 2023: 21.6.1",""}, + {"Misra C++ 2023: 21.6.2",""}, {"Misra C++ 2023: 21.6.3",""}, {"Misra C++ 2023: 21.6.4",""}, {"Misra C++ 2023: 21.6.5",""}, {"Misra C++ 2023: 22.3.1",""}, {"Misra C++ 2023: 22.4.1",""}, + {"Misra C++ 2023: 23.11.1",""}, {"Misra C++ 2023: 24.5.1",""}, {"Misra C++ 2023: 24.5.2",""}, {"Misra C++ 2023: 25.5.1",""}, {"Misra C++ 2023: 25.5.2",""}, {"Misra C++ 2023: 25.5.3",""}, + {"Misra C++ 2023: 26.3.1",""}, + {"Misra C++ 2023: 28.3.1",""}, {"Misra C++ 2023: 28.6.1",""}, {"Misra C++ 2023: 28.6.2",""}, + {"Misra C++ 2023: 30.0.1",""}, {"Misra C++ 2023: 30.0.2",""}, {"Misra C++ 2023: 4.1.1",""}, {"Misra C++ 2023: 4.1.2",""}, + {"Misra C++ 2023: 5.0.1",""}, + {"Misra C++ 2023: 5.13.1",""}, {"Misra C++ 2023: 5.13.2",""}, + {"Misra C++ 2023: 5.13.3",""}, + {"Misra C++ 2023: 5.13.4",""}, {"Misra C++ 2023: 5.13.5",""}, {"Misra C++ 2023: 5.13.6",""}, + {"Misra C++ 2023: 5.13.7",""}, + {"Misra C++ 2023: 5.7.1",""}, + {"Misra C++ 2023: 5.7.2",""}, {"Misra C++ 2023: 5.7.3",""}, + {"Misra C++ 2023: 6.0.1",""}, + {"Misra C++ 2023: 6.0.2",""}, + {"Misra C++ 2023: 6.0.3",""}, + {"Misra C++ 2023: 6.0.4",""}, + {"Misra C++ 2023: 6.2.2",""}, + {"Misra C++ 2023: 6.2.3",""}, + {"Misra C++ 2023: 6.2.4",""}, {"Misra C++ 2023: 6.4.2",""}, {"Misra C++ 2023: 6.4.3",""}, + {"Misra C++ 2023: 6.5.1",""}, + {"Misra C++ 2023: 6.5.2",""}, {"Misra C++ 2023: 6.7.1",""}, {"Misra C++ 2023: 6.7.2",""}, {"Misra C++ 2023: 6.8.3",""}, @@ -636,20 +701,42 @@ namespace checkers { {"Misra C++ 2023: 7.0.1",""}, {"Misra C++ 2023: 7.0.2",""}, {"Misra C++ 2023: 7.0.3",""}, + {"Misra C++ 2023: 7.0.4",""}, {"Misra C++ 2023: 7.0.5",""}, {"Misra C++ 2023: 7.0.6",""}, + {"Misra C++ 2023: 7.11.1",""}, + {"Misra C++ 2023: 7.11.2",""}, + {"Misra C++ 2023: 7.11.3",""}, + {"Misra C++ 2023: 8.0.1",""}, {"Misra C++ 2023: 8.1.1",""}, + {"Misra C++ 2023: 8.1.2",""}, + {"Misra C++ 2023: 8.14.1",""}, + {"Misra C++ 2023: 8.18.2",""}, + {"Misra C++ 2023: 8.19.1",""}, + {"Misra C++ 2023: 8.2.1",""}, + {"Misra C++ 2023: 8.2.10",""}, {"Misra C++ 2023: 8.2.11",""}, {"Misra C++ 2023: 8.2.2",""}, {"Misra C++ 2023: 8.2.3",""}, {"Misra C++ 2023: 8.2.4",""}, + {"Misra C++ 2023: 8.2.5",""}, + {"Misra C++ 2023: 8.2.6",""}, + {"Misra C++ 2023: 8.2.7",""}, {"Misra C++ 2023: 8.2.8",""}, {"Misra C++ 2023: 8.2.9",""}, + {"Misra C++ 2023: 8.20.1",""}, + {"Misra C++ 2023: 8.3.1",""}, {"Misra C++ 2023: 8.3.2",""}, {"Misra C++ 2023: 9.2.1",""}, + {"Misra C++ 2023: 9.3.1",""}, + {"Misra C++ 2023: 9.4.1",""}, + {"Misra C++ 2023: 9.4.2",""}, {"Misra C++ 2023: 9.5.1",""}, {"Misra C++ 2023: 9.5.2",""}, {"Misra C++ 2023: 9.6.1",""}, + {"Misra C++ 2023: 9.6.2",""}, + {"Misra C++ 2023: 9.6.3",""}, + {"Misra C++ 2023: 9.6.4",""}, {"Misra C: 1.4",""}, {"Misra C: 1.5",""}, {"Misra C: 10.1",""}, @@ -675,6 +762,7 @@ namespace checkers { {"Misra C: 17.11",""}, {"Misra C: 17.12",""}, {"Misra C: 17.13",""}, + {"Misra C: 17.2",""}, {"Misra C: 17.3",""}, {"Misra C: 17.4",""}, {"Misra C: 17.9",""}, @@ -720,6 +808,8 @@ namespace checkers { {"Misra C: 8.17",""}, {"Misra C: 8.3",""}, {"Misra C: 8.4",""}, + {"Misra C: 8.6",""}, + {"Misra C: 8.7",""}, {"Misra C: 8.8",""}, {"Misra C: 9.6",""}, {"Misra C: 9.7",""}, diff --git a/lib/checkio.cpp b/lib/checkio.cpp index 6c26967eb6a..71d5bfa9a43 100644 --- a/lib/checkio.cpp +++ b/lib/checkio.cpp @@ -411,9 +411,11 @@ void CheckIO::incompatibleFileOpenError(const Token *tok, const std::string &fil //--------------------------------------------------------------------------- void CheckIO::invalidScanf() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("invalidscanf")) return; + logChecker("CheckIO::invalidScanf"); + const SymbolDatabase * const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * scope : symbolDatabase->functionScopes) { for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { @@ -1710,7 +1712,7 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, nonneg int numFunction) { const Severity severity = numFormat > numFunction ? Severity::error : Severity::warning; - if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning)) + if (severity != Severity::error && !mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("wrongPrintfScanfArgNum")) return; std::ostringstream errmsg; @@ -1729,7 +1731,7 @@ void CheckIO::wrongPrintfScanfArgumentsError(const Token* tok, void CheckIO::wrongPrintfScanfPosixParameterPositionError(const Token* tok, const std::string& functionName, nonneg int index, nonneg int numFunction) { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("wrongPrintfScanfParameterPositionError")) return; std::ostringstream errmsg; errmsg << functionName << ": "; @@ -1992,7 +1994,7 @@ void CheckIO::argumentType(std::ostream& os, const ArgumentInfo * argInfo) void CheckIO::invalidLengthModifierError(const Token* tok, nonneg int numFormat, const std::string& modifier) { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("invalidLengthModifierError")) return; std::ostringstream errmsg; errmsg << "'" << modifier << "' in format string (no. " << numFormat << ") is a length modifier and cannot be used without a conversion specifier."; diff --git a/lib/checkmemoryleak.cpp b/lib/checkmemoryleak.cpp index 733e6053bcf..e9f7d4100aa 100644 --- a/lib/checkmemoryleak.cpp +++ b/lib/checkmemoryleak.cpp @@ -1093,9 +1093,14 @@ void CheckMemoryLeakNoVar::checkForUnusedReturnValue(const Scope *scope) void CheckMemoryLeakNoVar::checkForUnsafeArgAlloc(const Scope *scope) { // This test only applies to C++ source - if (!mTokenizer->isCPP() || !mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning)) + if (!mTokenizer->isCPP()) return; + if (!mSettings->isPremiumEnabled("leakUnsafeArgAlloc") && (!mSettings->certainty.isEnabled(Certainty::inconclusive) || !mSettings->severity.isEnabled(Severity::warning))) + return; + + logChecker("CheckMemoryLeakNoVar::checkForUnsafeArgAlloc"); + for (const Token *tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) { const Token *endParamToken = tok->next()->link(); diff --git a/lib/checknullpointer.cpp b/lib/checknullpointer.cpp index 9c5d25096b5..f2421df32cd 100644 --- a/lib/checknullpointer.cpp +++ b/lib/checknullpointer.cpp @@ -441,7 +441,7 @@ void CheckNullPointer::nullPointerError(const Token *tok, const std::string &var return; } - if (!mSettings->isEnabled(value, inconclusive)) + if (!mSettings->isEnabled(value, inconclusive) && !mSettings->isPremiumEnabled("nullPointer")) return; const ErrorPath errorPath = getErrorPath(tok, value, "Null pointer dereference"); diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 76364d83eb3..a2f54cbdd51 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -424,7 +424,9 @@ void CheckOther::invalidPointerCastError(const Token* tok, const std::string& fr void CheckOther::checkRedundantAssignment() { - if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("redundantAssignment")) + if (!mSettings->severity.isEnabled(Severity::style) && + !mSettings->isPremiumEnabled("redundantAssignment") && + !mSettings->isPremiumEnabled("redundantAssignInSwitch")) return; logChecker("CheckOther::checkRedundantAssignment"); // style @@ -773,7 +775,7 @@ void CheckOther::checkUnreachableCode() // misra-c-2023-2.1 // misra-cpp-2008-0-1-1 // autosar - if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unreachableCode")) + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("duplicateBreak") && !mSettings->isPremiumEnabled("unreachableCode")) return; logChecker("CheckOther::checkUnreachableCode"); // style @@ -1326,9 +1328,11 @@ static bool isVariableMutableInInitializer(const Token* start, const Token * end void CheckOther::checkConstVariable() { - if (!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) + if ((!mSettings->severity.isEnabled(Severity::style) || mTokenizer->isC()) && !mSettings->isPremiumEnabled("constVariable")) return; + logChecker("CheckOther::checkConstVariable"); // style,c++ + const SymbolDatabase *const symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Variable *var : symbolDatabase->variableList()) { @@ -1476,6 +1480,7 @@ void CheckOther::checkConstPointer() { if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("constParameter") && + !mSettings->isPremiumEnabled("constParameterReference") && !mSettings->isPremiumEnabled("constPointer")) return; @@ -3260,19 +3265,82 @@ void CheckOther::unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef Certainty::normal); } +static bool checkEvaluationOrderC(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings, bool & selfAssignmentError) +{ + // self assignment.. + if (tok2 == tok && tok->str() == "=" && parent->str() == "=" && isSameExpression(false, tok->astOperand1(), parent->astOperand1(), settings.library, true, false)) { + if (settings.severity.isEnabled(Severity::warning) && isSameExpression(true, tok->astOperand1(), parent->astOperand1(), settings.library, true, false)) + selfAssignmentError = true; + return false; + } + // Is expression used? + bool foundError = false; + visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { + if (tok3->str() == "&" && !tok3->astOperand2()) + return ChildrenToVisit::none; // don't handle address-of for now + if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) + return ChildrenToVisit::none; // don't care about sizeof usage + if (isSameExpression(false, tok->astOperand1(), tok3, settings.library, true, false)) + foundError = true; + return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); -void CheckOther::checkEvaluationOrder() + return foundError; +} + +static bool checkEvaluationOrderCpp11(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings) { - // This checker is not written according to C++11 sequencing rules - if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) - return; + if (tok->isAssignmentOp()) // TODO check assignment + return false; + if (tok->previous() == tok->astOperand1() && parent->isArithmeticalOp() && parent->isBinaryOp()) { + if (parent->astParent() && parent->astParent()->isAssignmentOp() && isSameExpression(false, tok->astOperand1(), parent->astParent()->astOperand1(), settings.library, true, false)) + return true; + } + bool foundUndefined{false}; + visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { + if (tok3->str() == "&" && !tok3->astOperand2()) + return ChildrenToVisit::none; // don't handle address-of for now + if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) + return ChildrenToVisit::none; // don't care about sizeof usage + if (isSameExpression(false, tok->astOperand1(), tok3, settings.library, true, false)) + foundUndefined = true; + return foundUndefined ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + + return foundUndefined; +} - logChecker("CheckOther::checkEvaluationOrder"); // C/C++03 +static bool checkEvaluationOrderCpp17(const Token * tok, const Token * tok2, const Token * parent, const Settings & settings, bool & foundUnspecified) +{ + if (tok->isAssignmentOp()) + return false; + bool foundUndefined{false}; + visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), [&](const Token *tok3) { + if (tok3->str() == "&" && !tok3->astOperand2()) + return ChildrenToVisit::none; // don't handle address-of for now + if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) + return ChildrenToVisit::none; // don't care about sizeof usage + if (isSameExpression(false, tok->astOperand1(), tok3, settings.library, true, false) && parent->isArithmeticalOp() && parent->isBinaryOp()) + foundUndefined = true; + if (tok3->tokType() == Token::eIncDecOp && isSameExpression(false, tok->astOperand1(), tok3->astOperand1(), settings.library, true, false)) { + if (parent->isArithmeticalOp() && parent->isBinaryOp()) + foundUndefined = true; + else + foundUnspecified = true; + } + return (foundUndefined || foundUnspecified) ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; + }); + + return foundUndefined || foundUnspecified; +} +void CheckOther::checkEvaluationOrder() +{ + logChecker("CheckOther::checkEvaluationOrder"); const SymbolDatabase *symbolDatabase = mTokenizer->getSymbolDatabase(); for (const Scope * functionScope : symbolDatabase->functionScopes) { for (const Token* tok = functionScope->bodyStart; tok != functionScope->bodyEnd; tok = tok->next()) { - if (tok->tokType() != Token::eIncDecOp && !tok->isAssignmentOp()) + if (!tok->isIncDecOp() && !tok->isAssignmentOp()) continue; if (!tok->astOperand1()) continue; @@ -3303,32 +3371,22 @@ void CheckOther::checkEvaluationOrder() if (parent->str() == "(" && parent->astOperand2()) break; - // self assignment.. - if (tok2 == tok && - tok->str() == "=" && - parent->str() == "=" && - isSameExpression(false, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) { - if (mSettings->severity.isEnabled(Severity::warning) && - isSameExpression(true, tok->astOperand1(), parent->astOperand1(), mSettings->library, true, false)) - selfAssignmentError(parent, tok->astOperand1()->expressionString()); - break; + bool foundError{false}, foundUnspecified{false}, bSelfAssignmentError{false}; + if (mTokenizer->isCPP() && mSettings->standards.cpp >= Standards::CPP11) { + if (mSettings->standards.cpp >= Standards::CPP17) + foundError = checkEvaluationOrderCpp17(tok, tok2, parent, *mSettings, foundUnspecified); + else + foundError = checkEvaluationOrderCpp11(tok, tok2, parent, *mSettings); } - - // Is expression used? - bool foundError = false; - visitAstNodes((parent->astOperand1() != tok2) ? parent->astOperand1() : parent->astOperand2(), - [&](const Token *tok3) { - if (tok3->str() == "&" && !tok3->astOperand2()) - return ChildrenToVisit::none; // don't handle address-of for now - if (tok3->str() == "(" && Token::simpleMatch(tok3->previous(), "sizeof")) - return ChildrenToVisit::none; // don't care about sizeof usage - if (isSameExpression(false, tok->astOperand1(), tok3, mSettings->library, true, false)) - foundError = true; - return foundError ? ChildrenToVisit::done : ChildrenToVisit::op1_and_op2; - }); + else + foundError = checkEvaluationOrderC(tok, tok2, parent, *mSettings, bSelfAssignmentError); if (foundError) { - unknownEvaluationOrder(parent); + unknownEvaluationOrder(parent, foundUnspecified); + break; + } + if (bSelfAssignmentError) { + selfAssignmentError(parent, tok->astOperand1()->expressionString()); break; } } @@ -3336,15 +3394,20 @@ void CheckOther::checkEvaluationOrder() } } -void CheckOther::unknownEvaluationOrder(const Token* tok) +void CheckOther::unknownEvaluationOrder(const Token* tok, bool isUnspecifiedBehavior) { - reportError(tok, Severity::error, "unknownEvaluationOrder", - "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); + isUnspecifiedBehavior ? + reportError(tok, Severity::portability, "unknownEvaluationOrder", + "Expression '" + (tok ? tok->expressionString() : std::string("x++, x++")) + "' depends on order of evaluation of side effects. Behavior is Unspecified according to c++17", CWE768, Certainty::normal) + : reportError(tok, Severity::error, "unknownEvaluationOrder", + "Expression '" + (tok ? tok->expressionString() : std::string("x = x++;")) + "' depends on order of evaluation of side effects", CWE768, Certainty::normal); } void CheckOther::checkAccessOfMovedVariable() { - if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11 || !mSettings->severity.isEnabled(Severity::warning)) + if (!mTokenizer->isCPP() || mSettings->standards.cpp < Standards::CPP11) + return; + if (!mSettings->isPremiumEnabled("accessMoved") && !mSettings->severity.isEnabled(Severity::warning)) return; logChecker("CheckOther::checkAccessOfMovedVariable"); // c++11,warning const bool reportInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); diff --git a/lib/checkother.h b/lib/checkother.h index 79b62e96988..f2726bfd4e3 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -280,7 +280,7 @@ class CPPCHECKLIB CheckOther : public Check { void redundantPointerOpError(const Token* tok, const std::string& varname, bool inconclusive, bool addressOfDeref); void raceAfterInterlockedDecrementError(const Token* tok); void unusedLabelError(const Token* tok, bool inSwitch, bool hasIfdef); - void unknownEvaluationOrder(const Token* tok); + void unknownEvaluationOrder(const Token* tok, bool isUnspecifiedBehavior = false); void accessMovedError(const Token *tok, const std::string &varname, const ValueFlow::Value *value, bool inconclusive); void funcArgNamesDifferent(const std::string & functionName, nonneg int index, const Token* declaration, const Token* definition); void funcArgOrderDifferent(const std::string & functionName, const Token * declaration, const Token * definition, const std::vector & declarations, const std::vector & definitions); diff --git a/lib/checksizeof.cpp b/lib/checksizeof.cpp index 50ddd100f8f..ffd9622bdc0 100644 --- a/lib/checksizeof.cpp +++ b/lib/checksizeof.cpp @@ -358,7 +358,7 @@ void CheckSizeof::sizeofCalculationError(const Token *tok, bool inconclusive) void CheckSizeof::sizeofFunction() { - if (!mSettings->severity.isEnabled(Severity::warning)) + if (!mSettings->severity.isEnabled(Severity::warning) && !mSettings->isPremiumEnabled("sizeofFunctionCall")) return; logChecker("CheckSizeof::sizeofFunction"); // warning diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 6732c4502cb..52483dcec01 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -1445,7 +1445,7 @@ void CheckUnusedVar::unassignedVariableError(const Token *tok, const std::string //--------------------------------------------------------------------------- void CheckUnusedVar::checkStructMemberUsage() { - if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedVariable")) + if (!mSettings->severity.isEnabled(Severity::style) && !mSettings->isPremiumEnabled("unusedStructMember") && !mSettings->isPremiumEnabled("unusedVariable")) return; logChecker("CheckUnusedVar::checkStructMemberUsage"); // style diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 17948db683a..2a2efd1dcbd 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -643,7 +643,8 @@ unsigned int CppCheck::checkFile(const std::string& filename, const std::string if (mUnusedFunctionsCheck && mSettings.useSingleJob() && mSettings.buildDir.empty()) { // this is not a real source file - we just want to tokenize it. treat it as C anyways as the language needs to be determined. Tokenizer tokenizer(mSettings, *this); - tokenizer.list.setLang(Standards::Language::C); + // enforce the language since markup files are special and do not adhere to the enforced language + tokenizer.list.setLang(Standards::Language::C, true); if (fileStream) { tokenizer.list.createTokens(*fileStream, filename); } diff --git a/lib/importproject.cpp b/lib/importproject.cpp index 47c55e09a44..e67ac4c118b 100644 --- a/lib/importproject.cpp +++ b/lib/importproject.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -567,6 +568,22 @@ namespace { TokenList tokenlist(&s); std::istringstream istr(c); tokenlist.createTokens(istr, Standards::Language::C); // TODO: check result + // TODO: put in a helper + // generate links + { + std::stack lpar; + for (Token* tok2 = tokenlist.front(); tok2; tok2 = tok2->next()) { + if (tok2->str() == "(") + lpar.push(tok2); + else if (tok2->str() == ")") { + if (lpar.empty()) + break; + Token::createMutualLinks(lpar.top(), tok2); + lpar.pop(); + } + } + } + tokenlist.createAst(); for (const Token *tok = tokenlist.front(); tok; tok = tok->next()) { if (tok->str() == "(" && tok->astOperand1() && tok->astOperand2()) { // TODO: this is wrong - it is Contains() not Equals() diff --git a/lib/settings.cpp b/lib/settings.cpp index e1780aa0d40..805556670d3 100644 --- a/lib/settings.cpp +++ b/lib/settings.cpp @@ -313,10 +313,11 @@ static const std::set autosarCheckers{ "comparePointers", "constParameter", "cstyleCast", - "ctuOneDefinitionViolation", + "ctuOneDefinitionRuleViolation", "doubleFree", "duplInheritedMember", "duplicateBreak", + "exceptThrowInDestructor", "funcArgNamesDifferent", "functionConst", "functionStatic", @@ -340,9 +341,11 @@ static const std::set autosarCheckers{ "redundantAssignment", "redundantInitialization", "returnDanglingLifetime", + "shadowArgument", + "shadowFunction", + "shadowVariable", "shiftTooManyBits", - "sizeofSideEffects", - "throwInDestructor", + "sizeofFunctionCall", "throwInNoexceptFunction", "uninitData", "uninitMember", @@ -353,7 +356,7 @@ static const std::set autosarCheckers{ "unusedStructMember", "unusedValue", "unusedVariable", - "useInitializerList", + "useInitializationList", "variableScope", "virtualCallInConstructor", "zerodiv", @@ -405,8 +408,11 @@ static const std::set certCppCheckers{ "accessMoved", "comparePointers", "containerOutOfBounds", - "ctuOneDefinitionViolation", - "deallocMismatch", + "ctuOneDefinitionRuleViolation", + "danglingLifetime", + "danglingReference", + "danglingTempReference", + "danglingTemporaryLifetime", "deallocThrow", "deallocuse", "doubleFree", @@ -414,11 +420,12 @@ static const std::set certCppCheckers{ "exceptThrowInDestructor", "initializerList", "invalidContainer", - "lifetime", "memleak", + "mismatchAllocDealloc", "missingReturn", "nullPointer", "operatorEqToSelf", + "returnDanglingLifetime", "sizeofCalculation", "uninitvar", "virtualCallInConstructor", @@ -433,10 +440,10 @@ static const std::set misrac2012Checkers{ "bufferAccessOutOfBounds", "comparePointers", "compareValueOutOfTypeRangeError", - "constPointer", + "constParameterPointer", "danglingLifetime", + "danglingTemporaryLifetime", "duplicateBreak", - "error", "funcArgNamesDifferent", "incompatibleFileOpen", "invalidFunctionArg", @@ -454,11 +461,14 @@ static const std::set misrac2012Checkers{ "redundantAssignment", "redundantCondition", "resourceLeak", + "returnDanglingLifetime", "shadowVariable", "sizeofCalculation", + "sizeofwithsilentarraypointer", "syntaxError", "uninitvar", "unknownEvaluationOrder", + "unreachableCode", "unreadVariable", "unusedLabel", "unusedVariable", @@ -474,10 +484,10 @@ static const std::set misrac2023Checkers{ "bufferAccessOutOfBounds", "comparePointers", "compareValueOutOfTypeRangeError", - "constPointer", + "constParameterPointer", "danglingLifetime", + "danglingTemporaryLifetime", "duplicateBreak", - "error", "funcArgNamesDifferent", "incompatibleFileOpen", "invalidFunctionArg", @@ -495,11 +505,14 @@ static const std::set misrac2023Checkers{ "redundantAssignment", "redundantCondition", "resourceLeak", + "returnDanglingLifetime", "shadowVariable", "sizeofCalculation", + "sizeofwithsilentarraypointer", "syntaxError", "uninitvar", "unknownEvaluationOrder", + "unreachableCode", "unreadVariable", "unusedLabel", "unusedVariable", @@ -513,7 +526,7 @@ static const std::set misracpp2008Checkers{ "constParameter", "constVariable", "cstyleCast", - "ctuOneDefinitionViolation", + "ctuOneDefinitionRuleViolation", "danglingLifetime", "duplInheritedMember", "duplicateBreak", @@ -522,7 +535,7 @@ static const std::set misracpp2008Checkers{ "functionConst", "functionStatic", "missingReturn", - "noExplicit", + "noExplicitConstructor", "overlappingWriteFunction", "overlappingWriteUnion", "pointerOutOfBounds", @@ -533,8 +546,7 @@ static const std::set misracpp2008Checkers{ "returnTempReference", "shadowVariable", "shiftTooManyBits", - "sizeofSideEffects", - "throwInDestructor", + "sizeofFunctionCall", "uninitDerivedMemberVar", "uninitDerivedMemberVarPrivate", "uninitMemberVar", @@ -549,11 +561,50 @@ static const std::set misracpp2008Checkers{ "unusedFunction", "unusedStructMember", "unusedVariable", - "varScope", "variableScope", "virtualCallInConstructor" }; +static const std::set misracpp2023Checkers{ + "accessForwarded", + "accessMoved", + "autoVariables", + "compareBoolExpressionWithInt", + "comparePointers", + "compareValueOutOfTypeRangeError", + "constParameter", + "constParameterReference", + "ctuOneDefinitionRuleViolation", + "danglingLifetime", + "identicalConditionAfterEarlyExit", + "identicalInnerCondition", + "ignoredReturnValue", + "invalidFunctionArg", + "invalidFunctionArgBool", + "invalidFunctionArgStr", + "knownConditionTrueFalse", + "missingReturn", + "noExplicitConstructor", + "operatorEqToSelf", + "overlappingWriteUnion", + "pointerOutOfBounds", + "pointerOutOfBoundsCond", + "preprocessorErrorDirective", + "redundantAssignInSwitch", + "redundantAssignment", + "redundantCopy", + "redundantInitialization", + "shadowVariable", + "subtractPointers", + "syntaxError", + "uninitMemberVar", + "uninitvar", + "unknownEvaluationOrder", + "unreachableCode", + "unreadVariable", + "virtualCallInConstructor" +}; + bool Settings::isPremiumEnabled(const char id[]) const { if (premiumArgs.find("autosar") != std::string::npos && autosarCheckers.count(id)) @@ -564,7 +615,9 @@ bool Settings::isPremiumEnabled(const char id[]) const return true; if (premiumArgs.find("misra-c-") != std::string::npos && (misrac2012Checkers.count(id) || misrac2023Checkers.count(id))) return true; - if (premiumArgs.find("misra-c++") != std::string::npos && misracpp2008Checkers.count(id)) + if (premiumArgs.find("misra-c++-2008") != std::string::npos && misracpp2008Checkers.count(id)) + return true; + if (premiumArgs.find("misra-c++-2023") != std::string::npos && misracpp2023Checkers.count(id)) return true; return false; } diff --git a/lib/tokenlist.cpp b/lib/tokenlist.cpp index 25d1764f830..7795dfba990 100644 --- a/lib/tokenlist.cpp +++ b/lib/tokenlist.cpp @@ -95,6 +95,7 @@ void TokenList::determineCppC() { // only try to determine if it wasn't enforced if (mLang == Standards::Language::None) { + ASSERT_LANG(!getSourceFilePath().empty()); mLang = Path::identify(getSourceFilePath()); // TODO: cannot enable assert as this might occur for unknown extensions //ASSERT_LANG(mLang != Standards::Language::None); @@ -372,11 +373,11 @@ bool TokenList::createTokensInternal(std::istream &code, const std::string& file // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) void TokenList::createTokens(simplecpp::TokenList&& tokenList) { - if (tokenList.cfront()) { + // tokenList.cfront() might be NULL if the file contained nothing to tokenize so we need to check the files instead + if (!tokenList.getFiles().empty()) { // this is a copy - // TODO: the same as TokenList.files - move that instead // TODO: this points to mFiles when called from createTokens(std::istream &, const std::string&) - mOrigFiles = mFiles = tokenList.cfront()->location.files; + mOrigFiles = mFiles = tokenList.getFiles(); } else mFiles.clear(); @@ -2180,10 +2181,13 @@ bool TokenList::isCPP() const return mLang == Standards::Language::CPP; } -void TokenList::setLang(Standards::Language lang) +void TokenList::setLang(Standards::Language lang, bool force) { ASSERT_LANG(lang != Standards::Language::None); - ASSERT_LANG(mLang == Standards::Language::None); + if (!force) + { + ASSERT_LANG(mLang == Standards::Language::None); + } mLang = lang; } diff --git a/lib/tokenlist.h b/lib/tokenlist.h index c03cf3b6382..66bb008b783 100644 --- a/lib/tokenlist.h +++ b/lib/tokenlist.h @@ -68,7 +68,8 @@ class CPPCHECKLIB TokenList { /** @return true if the code is C++ */ bool isCPP() const; - void setLang(Standards::Language lang); + // TODO: get rid of this + void setLang(Standards::Language lang, bool force = false); /** * Delete all tokens in given token list diff --git a/lib/version.h b/lib/version.h index d99b949bd04..78aded14c45 100644 --- a/lib/version.h +++ b/lib/version.h @@ -7,7 +7,7 @@ #define CPPCHECK_MAJOR_VERSION 2 #define CPPCHECK_MINOR_VERSION 14 #define CPPCHECK_DEVMINOR_VERSION 14 -#define CPPCHECK_BUGFIX_VERSION 0 +#define CPPCHECK_BUGFIX_VERSION 1 #define STRINGIFY(x) STRING(x) #define STRING(VER) #VER diff --git a/test/cli/other_test.py b/test/cli/other_test.py index 1ac8e4a341e..5fd2e2b82fe 100644 --- a/test/cli/other_test.py +++ b/test/cli/other_test.py @@ -1357,4 +1357,26 @@ def test_rule(tmpdir): lines = stderr.splitlines() assert lines == [ "{}:4:0: style: found 'f' [rule]".format(test_file) - ] \ No newline at end of file + ] + + +def test_markup_lang(tmpdir): + test_file_1 = os.path.join(tmpdir, 'test_1.qml') + with open(test_file_1, 'wt') as f: + pass + test_file_2 = os.path.join(tmpdir, 'test_2.cpp') + with open(test_file_2, 'wt') as f: + pass + + # do not assert processing markup file with enforced language + args = [ + '--library=qt', + '--enable=unusedFunction', + '--language=c++', + '-j1', + test_file_1, + test_file_2 + ] + + exitcode, stdout, _ = cppcheck(args) + assert exitcode == 0, stdout diff --git a/test/testother.cpp b/test/testother.cpp index 5faec17d2c5..213712b0011 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -10799,6 +10799,28 @@ class TestOther : public TestFixture { ASSERT_EQUALS("[test.cpp:6]: (style) Label 'label' is not used.\n", errout_str()); } + #define checkCustomSettings(...) checkCustomSettings_(__FILE__, __LINE__, __VA_ARGS__) + void checkCustomSettings_(const char* file, int line, const char code[], bool cpp = true, bool inconclusive = true, bool runSimpleChecks=true, bool verbose=false, Settings* settings = nullptr) { + if (!settings) { + settings = &_settings; + } + settings->certainty.setEnabled(Certainty::inconclusive, inconclusive); + settings->verbose = verbose; + + // Tokenize.. + SimpleTokenizer tokenizer(*settings, *this); + ASSERT_LOC(tokenizer.tokenize(code, cpp), file, line); + + // Check.. + runChecks(tokenizer, this); + + (void)runSimpleChecks; // TODO Remove this + } + + void checkCustomSettings_(const char* file, int line, const char code[], Settings *s) { + checkCustomSettings_(file, line, code, true, true, true, false, s); + } + void testEvaluationOrder() { check("void f() {\n" " int x = dostuff();\n" @@ -10842,6 +10864,29 @@ class TestOther : public TestFixture { " a[x+y] = a[y+x]++;;\n" "}\n", false); ASSERT_EQUALS("[test.c:3]: (error) Expression 'a[x+y]=a[y+x]++' depends on order of evaluation of side effects\n", errout_str()); + + check("void f(int i) {\n" + " int n = ++i + i;\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Expression '++i+i' depends on order of evaluation of side effects\n", errout_str()); + + check("long int f1(const char *exp) {\n" + " return dostuff(++exp, ++exp, 10);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (portability) Expression '++exp,++exp' depends on order of evaluation of side effects. Behavior is Unspecified according to c++17\n" + "[test.cpp:2]: (portability) Expression '++exp,++exp' depends on order of evaluation of side effects. Behavior is Unspecified according to c++17\n", errout_str()); + + check("void f(int i) {\n" + " int n = (~(-(++i)) + i);\n" + "}"); + ASSERT_EQUALS("[test.cpp:2]: (error) Expression '~(-(++i))+i' depends on order of evaluation of side effects\n", errout_str()); + + /*const*/ Settings settings11 = settingsBuilder(_settings).cpp(Standards::CPP11).build(); + + checkCustomSettings("void f(int i) {\n" + " i = i++ + 2;\n" + "}", &settings11); + ASSERT_EQUALS("[test.cpp:2]: (error) Expression 'i+++2' depends on order of evaluation of side effects\n", errout_str()); } void testEvaluationOrderSelfAssignment() { diff --git a/test/testtokenlist.cpp b/test/testtokenlist.cpp index 2e730cc6360..4b11c5c37db 100644 --- a/test/testtokenlist.cpp +++ b/test/testtokenlist.cpp @@ -20,6 +20,7 @@ #include "fixture.h" #include "helpers.h" #include "platform.h" +#include "preprocessor.h" #include "standards.h" #include "token.h" #include "tokenlist.h" @@ -27,6 +28,8 @@ #include #include +#include + class TestTokenList : public TestFixture { public: TestTokenList() : TestFixture("TestTokenList") {} @@ -39,6 +42,7 @@ class TestTokenList : public TestFixture { TEST_CASE(testaddtoken2); TEST_CASE(inc); TEST_CASE(isKeyword); + TEST_CASE(notokens); } // inspired by #5895 @@ -144,6 +148,19 @@ class TestTokenList : public TestFixture { ASSERT_EQUALS(false, tokenlist.front()->isKeyword()); } } + + void notokens() { + // analyzing /usr/include/poll.h caused Path::identify() to be called with an empty filename from + // TokenList::determineCppC() because there are no tokens + const char code[] = "#include "; + std::istringstream istr(code); + std::vector files; + simplecpp::TokenList tokens1(istr, files, "poll.h", nullptr); + Preprocessor preprocessor(settingsDefault, *this); + simplecpp::TokenList tokensP = preprocessor.preprocess(tokens1, "", files, true); + TokenList tokenlist(&settingsDefault); + tokenlist.createTokens(std::move(tokensP)); // do not assert + } }; REGISTER_TEST(TestTokenList) diff --git a/tools/get_checkers.py b/tools/get_checkers.py index ca0b8427689..4b7c7b3ea64 100644 --- a/tools/get_checkers.py +++ b/tools/get_checkers.py @@ -1,10 +1,11 @@ +import datetime import glob import os import re print("""/* * Cppcheck - A tool for static C/C++ code analysis - * Copyright (C) 2007-2023 Cppcheck team. + * Copyright (C) 2007-%i 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 @@ -26,7 +27,7 @@ #include "checkers.h" namespace checkers { - const std::map allCheckers{""") + const std::map allCheckers{""" % (datetime.date.today().year,)) for filename in glob.glob(os.path.expanduser('~/cppchecksolutions/cppcheck/lib/*.cpp')): for line in open(filename,'rt'): diff --git a/win_installer/productInfo.wxi b/win_installer/productInfo.wxi index 870130db69e..b39b8c15f27 100644 --- a/win_installer/productInfo.wxi +++ b/win_installer/productInfo.wxi @@ -1,8 +1,8 @@ - + - +