diff --git a/Makefile b/Makefile index 4a4355226df..8cceaf6182a 100644 --- a/Makefile +++ b/Makefile @@ -345,6 +345,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 \ @@ -935,6 +936,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/cli/cmdlineparser.cpp b/cli/cmdlineparser.cpp index e79576c4ad3..3f9850e703a 100644 --- a/cli/cmdlineparser.cpp +++ b/cli/cmdlineparser.cpp @@ -1252,12 +1252,7 @@ CmdLineParser::Result CmdLineParser::parseFromArgs(int argc, const char* const a // --std else if (std::strncmp(argv[i], "--std=", 6) == 0) { const std::string std = argv[i] + 6; - Standards tmp; - if (tmp.setC(std)) { - mSettings.standards.c = tmp.c; - } else if (tmp.setCPP(std)) { - mSettings.standards.cpp = tmp.cpp; - } else { + if (!mSettings.standards.setStd(std)) { mLogger.printError("unknown --std value '" + std + "'"); return Result::Fail; } diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp index f05cdfc8248..6d096300071 100644 --- a/lib/cppcheck.cpp +++ b/lib/cppcheck.cpp @@ -449,9 +449,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 + " "; diff --git a/lib/standards.h b/lib/standards.h index 4965e432fcd..416f6e6faf9 100644 --- a/lib/standards.h +++ b/lib/standards.h @@ -44,16 +44,27 @@ 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); - c = getC(str); - return !stdValue.empty() && str == getC(); + if (str.empty()) + return false; + const cstd_t c_new = getC(str); + const bool b = (str == getC(c_new)); + if (b) { + c = c_new; + stdValueC = std::move(str); + } + return b; } std::string getC() const { - switch (c) { + return getC(c); + } + static std::string getC(cstd_t c_std) { + switch (c_std) { case C89: return "c89"; case C99: @@ -86,10 +97,15 @@ struct Standards { return Standards::CLatest; } bool setCPP(std::string str) { - stdValue = str; - strTolower(str); - cpp = getCPP(str); - return !stdValue.empty() && str == getCPP(); + if (str.empty()) + return false; + const cppstd_t cpp_new = getCPP(str); + const bool b = (str == getCPP(cpp_new)); + if (b) { + cpp = cpp_new; + stdValueCPP = std::move(str); + } + return b; } std::string getCPP() const { return getCPP(cpp); @@ -137,6 +153,9 @@ struct Standards { } return Standards::CPPLatest; } + bool setStd(const std::string& str) { + return setC(str) || setCPP(str); + } }; /// @} diff --git a/test/cli/clang-import_test.py b/test/cli/clang-import_test.py index 02b0d525d5d..aa92a1af7f4 100644 --- a/test/cli/clang-import_test.py +++ b/test/cli/clang-import_test.py @@ -192,22 +192,18 @@ def test_cmd_enforce_cpp(tmp_path): # #13128 __test_cmd(tmp_path, 'test.c',['-x', 'c++'], '-x c++') -@pytest.mark.xfail(strict=True) def test_cmd_std_c(tmp_path): # #13129 __test_cmd(tmp_path, 'test.cpp',['--std=c89', '--std=c++14'], '-x c++ -std=c++14') -@pytest.mark.xfail(strict=True) def test_cmd_std_cpp(tmp_path): # #13129 __test_cmd(tmp_path, 'test.c',['--std=c89', '--std=c++14'], '-x c -std=c89') -@pytest.mark.xfail(strict=True) def test_cmd_std_c_enforce(tmp_path): # #13128/#13129 __test_cmd(tmp_path, 'test.cpp',['--language=c', '--std=c89', '--std=c++14'], '-x c -std=c89') -@pytest.mark.xfail(strict=True) def test_cmd_std_cpp_enforce(tmp_path): # #13128/#13129 __test_cmd(tmp_path, 'test.c',['--language=c++', '--std=c89', '--std=c++14'], '-x c++ -std=c++14') diff --git a/test/fixture.h b/test/fixture.h index 5ceb8699e66..450d1606c3a 100644 --- a/test/fixture.h +++ b/test/fixture.h @@ -90,6 +90,14 @@ class TestFixture : public ErrorLogger { assertEquals(filename, linenr, static_cast(expected), static_cast(actual), msg); } + template + void todoAssertEqualsEnum(const char* const filename, const unsigned int linenr, const T& wanted, const T& current, const T& actual) const { + if (std::is_unsigned()) + todoAssertEquals(filename, linenr, static_cast(wanted), static_cast(current), static_cast(actual)); + else + todoAssertEquals(filename, linenr, static_cast(wanted), static_cast(current), static_cast(actual)); + } + void assertEquals(const char * filename, unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; void assertEqualsWithoutLineNumbers(const char * filename, unsigned int linenr, const std::string &expected, const std::string &actual, const std::string &msg = emptyString) const; void assertEquals(const char * filename, unsigned int linenr, const char expected[], const std::string& actual, const std::string &msg = emptyString) const; @@ -308,6 +316,7 @@ class TestInstance { #define ASSERT_EQUALS_LOC_MSG( EXPECTED, ACTUAL, MSG, FILE_, LINE_ ) assertEquals(FILE_, LINE_, EXPECTED, ACTUAL, MSG) #define ASSERT_EQUALS_MSG( EXPECTED, ACTUAL, MSG ) assertEquals(__FILE__, __LINE__, EXPECTED, ACTUAL, MSG) #define ASSERT_EQUALS_ENUM( EXPECTED, ACTUAL ) assertEqualsEnum(__FILE__, __LINE__, (EXPECTED), (ACTUAL)) +#define TODO_ASSERT_EQUALS_ENUM( WANTED, CURRENT, ACTUAL ) todoAssertEqualsEnum(__FILE__, __LINE__, WANTED, CURRENT, ACTUAL) #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) { 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) { 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..7f4a6cccc07 --- /dev/null +++ b/test/teststandards.cpp @@ -0,0 +1,144 @@ +/* + * 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(setAlias1); + TEST_CASE(setAlias2); + TEST_CASE(getC); + TEST_CASE(getCPP); + TEST_CASE(setStd); + } + + 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::C17, Standards::CLatest, Standards::getC("gnu17")); + TODO_ASSERT_EQUALS_ENUM(Standards::C11, Standards::CLatest, Standards::getC("iso9899:2011")); + + 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::CPPLatest, Standards::getCPP("C++11")); + } + + void setStd() const { + Standards stds; + ASSERT(stds.setStd("c11")); + ASSERT(stds.setStd("c++11")); + TODO_ASSERT(stds.setStd("gnu11")); + TODO_ASSERT(stds.setStd("gnu++17")); + TODO_ASSERT(stds.setStd("iso9899:2011")); + + ASSERT(!stds.setStd("C++11")); + ASSERT(!stds.setStd("Gnu++11")); + } +}; + +REGISTER_TEST(TestStandards)