diff --git a/Makefile b/Makefile index 7124e4f9482..2d3061c9baa 100644 --- a/Makefile +++ b/Makefile @@ -343,6 +343,7 @@ TESTOBJ = test/fixture.o \ test/testsimplifyusing.o \ test/testsingleexecutor.o \ test/testsizeof.o \ + test/teststandards.o \ test/teststl.o \ test/teststring.o \ test/testsummaries.o \ @@ -927,6 +928,9 @@ test/testsingleexecutor.o: test/testsingleexecutor.cpp cli/executor.h cli/single test/testsizeof.o: test/testsizeof.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checksizeof.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/testsizeof.cpp +test/teststandards.o: test/teststandards.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/utils.h test/fixture.h + $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/teststandards.cpp + test/teststl.o: test/teststl.cpp externals/simplecpp/simplecpp.h lib/addoninfo.h lib/check.h lib/checkstl.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/library.h lib/mathlib.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/tokenize.h lib/tokenlist.h lib/utils.h lib/vfvalue.h test/fixture.h test/helpers.h $(CXX) ${INCLUDE_FOR_TEST} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ test/teststl.cpp diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index 4c3bcb7d0b6..b3ebc72b011 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -446,9 +446,10 @@ unsigned int CppCheck::checkClang(const FileWithDetails &file) #endif std::string flags(langOpt + " "); - // TODO: does not apply C standard - if (isCpp && !mSettings.standards.stdValue.empty()) - flags += "-std=" + mSettings.standards.stdValue + " "; + if (isCpp && !mSettings.standards.stdValueCPP.empty()) + flags += "-std=" + mSettings.standards.stdValueCPP + " "; + if (!isCpp && !mSettings.standards.stdValueC.empty()) + flags += "-std=" + mSettings.standards.stdValueC + " "; for (const std::string &i: mSettings.includePaths) flags += "-I" + i + " "; @@ -581,10 +582,7 @@ unsigned int CppCheck::check(const FileSettings &fs) temp.mSettings.userDefines += fs.cppcheckDefines(); temp.mSettings.includePaths = fs.includePaths; temp.mSettings.userUndefs.insert(fs.undefs.cbegin(), fs.undefs.cend()); - if (fs.standard.find("++") != std::string::npos) - temp.mSettings.standards.setCPP(fs.standard); - else if (!fs.standard.empty()) - temp.mSettings.standards.setC(fs.standard); + temp.mSettings.standards.setStd(fs.standard); if (fs.platformType != Platform::Type::Unspecified) temp.mSettings.platform.set(fs.platformType); if (mSettings.clang) { diff --git a/lib/preprocessor.cpp b/lib/preprocessor.cpp index eeb96362898..56c1df352fb 100644 --- a/lib/preprocessor.cpp +++ b/lib/preprocessor.cpp @@ -711,16 +711,21 @@ static simplecpp::DUI createDUI(const Settings &mSettings, const std::string &cf dui.undefined = mSettings.userUndefs; // -U dui.includePaths = mSettings.includePaths; // -I dui.includes = mSettings.userIncludes; // --include - // TODO: use mSettings.standards.stdValue instead // TODO: error out on unknown language? const Standards::Language lang = Path::identify(filename, mSettings.cppHeaderProbe); if (lang == Standards::Language::CPP) { - dui.std = mSettings.standards.getCPP(); - splitcfg(mSettings.platform.getLimitsDefines(Standards::getCPP(dui.std)), dui.defines, ""); + if (!mSettings.standards.stdValueCPP.empty()) + dui.std = mSettings.standards.stdValueCPP; + else + dui.std = mSettings.standards.getCPP(); + splitcfg(mSettings.platform.getLimitsDefines(mSettings.standards.cpp), dui.defines, ""); } else if (lang == Standards::Language::C) { - dui.std = mSettings.standards.getC(); - splitcfg(mSettings.platform.getLimitsDefines(Standards::getC(dui.std)), dui.defines, ""); + if (!mSettings.standards.stdValueC.empty()) + dui.std = mSettings.standards.stdValueC; + else + dui.std = mSettings.standards.getC(); + splitcfg(mSettings.platform.getLimitsDefines(mSettings.standards.c), dui.defines, ""); } dui.clearIncludeCache = mSettings.clearIncludeCache; return dui; diff --git a/lib/standards.h b/lib/standards.h index 4965e432fcd..d708d744d19 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -44,13 +44,19 @@ struct Standards { enum cppstd_t : std::uint8_t { CPP03, CPP11, CPP14, CPP17, CPP20, CPP23, CPP26, CPPLatest = CPP26 } cpp = CPPLatest; /** --std value given on command line */ - std::string stdValue; + std::string stdValueC; + + /** --std value given on command line */ + std::string stdValueCPP; bool setC(std::string str) { - stdValue = str; - strTolower(str); + if (str.empty()) + return false; c = getC(str); - return !stdValue.empty() && str == getC(); + bool b = (str == getC()); + if (b) + stdValueC = std::move(str); + return b; } std::string getC() const { switch (c) { @@ -86,10 +92,13 @@ struct Standards { return Standards::CLatest; } bool setCPP(std::string str) { - stdValue = str; - strTolower(str); + if (str.empty()) + return false; cpp = getCPP(str); - return !stdValue.empty() && str == getCPP(); + bool b = (str == getCPP()); + if (b) + stdValueCPP = std::move(str); + return b; } std::string getCPP() const { return getCPP(cpp); @@ -137,6 +146,13 @@ struct Standards { } return Standards::CPPLatest; } + bool setStd(const std::string& str) { + if (setC(str)) + return true; + if (setCPP(str)) + return true; + return false; + } }; /// @} diff --git a/test/fixture.h b/test/fixture.h index c26d20ef696..7a18698ca21 100644 --- a/test/fixture.h +++ b/test/fixture.h @@ -90,6 +90,13 @@ class TestFixture : public ErrorLogger { return assertEquals(filename, linenr, static_cast(expected), static_cast(actual), msg); } + template + bool todoAssertEqualsEnum(const char* const filename, const unsigned int linenr, const T& wanted, const T& current, const T& actual, const std::string& msg = emptyString) const { + if (std::is_unsigned()) + return todoAssertEquals(filename, linenr, static_cast(wanted), static_cast(current), static_cast(actual), msg); + return todoAssertEquals(filename, linenr, static_cast(wanted), static_cast(current), static_cast(actual), msg); + } + //Helper function to be called when an assertEquals assertion fails. //Writes the appropriate failure message to errmsg and increments fails_counter void assertEqualsFailed(const char* filename, unsigned int linenr, const std::string& expected, const std::string& actual, const std::string& msg) const; @@ -308,6 +315,7 @@ class TestInstance { #define ASSERT_EQUALS_LOC_MSG( EXPECTED, ACTUAL, MSG, FILE_, LINE_ ) (void)assertEquals(FILE_, LINE_, EXPECTED, ACTUAL, MSG) #define ASSERT_EQUALS_MSG( EXPECTED, ACTUAL, MSG ) (void)assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL, MSG) #define ASSERT_EQUALS_ENUM( EXPECTED, ACTUAL ) if (!assertEqualsEnum(__FILE__, __LINE__, (EXPECTED), (ACTUAL))) return +#define TODO_ASSERT_EQUALS_ENUM( WANTED, CURRENT, ACTUAL ) if (!todoAssertEqualsEnum(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL)) return #define ASSERT_THROW( CMD, EXCEPTION ) do { try { (void)(CMD); assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&) {} catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) #define ASSERT_THROW_EQUALS( CMD, EXCEPTION, EXPECTED ) do { try { (void)(CMD); assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&e) { (void)assertEquals(__FILE__, __LINE__, EXPECTED, e.errorMessage); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) #define ASSERT_THROW_EQUALS_2( CMD, EXCEPTION, EXPECTED ) do { try { (void)(CMD); assertThrowFail(__FILE__, __LINE__); } catch (const EXCEPTION&e) { (void)assertEquals(__FILE__, __LINE__, EXPECTED, e.what()); } catch (...) { assertThrowFail(__FILE__, __LINE__); } } while (false) diff --git a/test/testrunner.vcxproj b/test/testrunner.vcxproj index 66dc928a094..5951a2770ba 100755 --- a/test/testrunner.vcxproj +++ b/test/testrunner.vcxproj @@ -91,6 +91,7 @@ + diff --git a/test/teststandards.cpp b/test/teststandards.cpp new file mode 100644 index 00000000000..fa12218c1ff --- /dev/null +++ b/test/teststandards.cpp @@ -0,0 +1,129 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 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 "fixture.h" +#include "standards.h" + +class TestStandards : public TestFixture { +public: + TestStandards() : TestFixture("TestStandards") {} + +private: + void run() override { + TEST_CASE(set); + TEST_CASE(setAlias); + TEST_CASE(getC); + TEST_CASE(getCPP); + } + + void set() const { + Standards stds; + ASSERT_EQUALS_ENUM(Standards::CLatest, stds.c); + ASSERT_EQUALS("", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPPLatest, stds.cpp); + ASSERT_EQUALS("", stds.stdValueCPP); + + ASSERT_EQUALS(true, stds.setC("c99")); + ASSERT_EQUALS_ENUM(Standards::C99, stds.c); + ASSERT_EQUALS("c99", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPPLatest, stds.cpp); + ASSERT_EQUALS("", stds.stdValueCPP); + + ASSERT_EQUALS(true, stds.setC("c11")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPPLatest, stds.cpp); + ASSERT_EQUALS("", stds.stdValueCPP); + + ASSERT_EQUALS(true, stds.setCPP("c++11")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP11, stds.cpp); + ASSERT_EQUALS("c++11", stds.stdValueCPP); + + ASSERT_EQUALS(true, stds.setCPP("c++23")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP23, stds.cpp); + ASSERT_EQUALS("c++23", stds.stdValueCPP); + + ASSERT_EQUALS(false, stds.setC("c77")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP23, stds.cpp); + ASSERT_EQUALS("c++23", stds.stdValueCPP); + + ASSERT_EQUALS(false, stds.setCPP("c+77")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP23, stds.cpp); + ASSERT_EQUALS("c++23", stds.stdValueCPP); + + ASSERT_EQUALS(false, stds.setC("C23")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP23, stds.cpp); + ASSERT_EQUALS("c++23", stds.stdValueCPP); + + ASSERT_EQUALS(false, stds.setCPP("C++11")); + ASSERT_EQUALS_ENUM(Standards::C11, stds.c); + ASSERT_EQUALS("c11", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPP23, stds.cpp); + ASSERT_EQUALS("c++23", stds.stdValueCPP); + } + + void setAlias1() const { + Standards stds; + TODO_ASSERT_EQUALS(true, false, stds.setCPP("gnu++11")); + ASSERT_EQUALS_ENUM(Standards::CLatest, stds.c); + ASSERT_EQUALS("", stds.stdValueC); + TODO_ASSERT_EQUALS_ENUM(Standards::CPP11, Standards::CPPLatest, stds.cpp); + TODO_ASSERT_EQUALS("gnu++11", "", stds.stdValueCPP); + } + + void setAlias2() const { + Standards stds; + TODO_ASSERT_EQUALS(true, false, stds.setC("gnu17")); + TODO_ASSERT_EQUALS_ENUM(Standards::C17, Standards::CLatest, stds.c); + TODO_ASSERT_EQUALS("gnu17", "", stds.stdValueC); + ASSERT_EQUALS_ENUM(Standards::CPPLatest, stds.cpp); + ASSERT_EQUALS("", stds.stdValueCPP); + } + + void getC() const { + ASSERT_EQUALS_ENUM(Standards::C99, Standards::getC("c99")); + ASSERT_EQUALS_ENUM(Standards::C11, Standards::getC("c11")); + TODO_ASSERT_EQUALS_ENUM(Standards::C23, Standards::CLatest, Standards::getC("gnu23")); + + ASSERT_EQUALS_ENUM(Standards::CLatest, Standards::getC("")); + ASSERT_EQUALS_ENUM(Standards::CLatest, Standards::getC("c77")); + ASSERT_EQUALS_ENUM(Standards::CLatest, Standards::getC("C99")); + } + + void getCPP() const { + ASSERT_EQUALS_ENUM(Standards::CPP11, Standards::getCPP("c++11")); + ASSERT_EQUALS_ENUM(Standards::CPP23, Standards::getCPP("c++23")); + TODO_ASSERT_EQUALS_ENUM(Standards::CPP23, Standards::CPPLatest, Standards::getCPP("gnu++23")); + + ASSERT_EQUALS_ENUM(Standards::CPPLatest, Standards::getCPP("")); + ASSERT_EQUALS_ENUM(Standards::CPPLatest, Standards::getCPP("c++77")); + ASSERT_EQUALS_ENUM(Standards::CPP11, Standards::getCPP("C++11")); + } +}; + +REGISTER_TEST(TestStandards)