diff --git a/lib/token.cpp b/lib/token.cpp index aee3150bd19..1188a75c6f6 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -729,29 +729,10 @@ nonneg int Token::getStrLength(const Token *tok) assert(tok != nullptr); assert(tok->mTokType == eString); - int len = 0; - // cppcheck-suppress shadowFunction - TODO: fix this - const std::string str(getStringLiteral(tok->str())); - std::string::const_iterator it = str.cbegin(); - const std::string::const_iterator end = str.cend(); - - while (it != end) { - if (*it == '\\') { - ++it; - - // string ends at '\0' - if (*it == '0') - return len; - } - - if (*it == '\0') - return len; - - ++it; - ++len; - } + const std::string s(replaceEscapeSequences(getStringLiteral(tok->str()))); - return len; + const auto pos = s.find('\0'); + return pos < s.size() ? pos : s.size(); } nonneg int Token::getStrArraySize(const Token *tok) diff --git a/lib/token.h b/lib/token.h index c9e8c68ee4a..1d538bf2284 100644 --- a/lib/token.h +++ b/lib/token.h @@ -751,14 +751,13 @@ class CPPCHECKLIB Token { bool isCChar() const { return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', emptyString)) || - ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && mStr.length() == 3) || - ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && mStr.compare(0, 2, "\'\\") == 0 && mStr.length() == 4)); + ((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && (replaceEscapeSequences(getCharLiteral(mStr)).size() == 1))); } bool isCMultiChar() const { - return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString)) && (mStr.compare(0, 2, "\'\\") != 0 || mStr.length() > 4) && - (mStr.length() > 3)); + return (mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && (replaceEscapeSequences(getCharLiteral(mStr)).size() > 1); } + /** * @brief Is current token a template argument? * diff --git a/lib/utils.cpp b/lib/utils.cpp index ff52298deb6..59f85b620ea 100644 --- a/lib/utils.cpp +++ b/lib/utils.cpp @@ -147,3 +147,40 @@ void findAndReplace(std::string &source, const std::string &searchFor, const std index += replaceWith.length(); } } + +std::string replaceEscapeSequences(const std::string &source) { + std::string result; + result.reserve(source.size()); + for (std::size_t i = 0; i < source.size(); ++i) { + if (source[i] != '\\' || i + 1 >= source.size()) + result += source[i]; + else { + ++i; + if (source[i] == 'n') { + result += '\n'; + } else if (source[i] == 'r') { + result += '\r'; + } else if (source[i] == 't') { + result += '\t'; + } else if (source[i] == 'x') { + std::string value = "0"; + if (i + 1 < source.size() && std::isxdigit(source[i+1])) + value += source[i++ + 1]; + if (i + 1 < source.size() && std::isxdigit(source[i+1])) + value += source[i++ + 1]; + result += static_cast(std::stoi(value, nullptr, 16)); + } else if (source[i] == '0') { + std::string value = "0"; + if (i + 1 < source.size() && source[i+1] >= '0' && source[i+1] <= '7') + value += source[i++ + 1]; + if (i + 1 < source.size() && source[i+1] >= '0' && source[i+1] <= '7') + value += source[i++ + 1]; + result += static_cast(std::stoi(value, nullptr, 8)); + } else { + result += source[i]; + } + } + } + return result; +} + diff --git a/lib/utils.h b/lib/utils.h index 34502736863..11b3fa815f2 100644 --- a/lib/utils.h +++ b/lib/utils.h @@ -364,6 +364,12 @@ CPPCHECKLIB std::string trim(const std::string& s, const std::string& t = " \t") */ CPPCHECKLIB void findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith); +/** + * Replace all escape sequences in the given string. + * @param source The string that contains escape sequences + */ +CPPCHECKLIB std::string replaceEscapeSequences(const std::string &source); + namespace cppcheck { NORETURN inline void unreachable() diff --git a/test/testtoken.cpp b/test/testtoken.cpp index 238bff97bae..0f4e8bd0563 100644 --- a/test/testtoken.cpp +++ b/test/testtoken.cpp @@ -371,6 +371,14 @@ class TestToken : public TestFixture { ASSERT_EQUALS(false, tok.isUtf32()); ASSERT_EQUALS(false, tok.isLong()); ASSERT_EQUALS(true, tok.isCMultiChar()); + + tok.str("'\\x10'"); + ASSERT_EQUALS(true, tok.isCChar()); + ASSERT_EQUALS(false, tok.isUtf8()); + ASSERT_EQUALS(false, tok.isUtf16()); + ASSERT_EQUALS(false, tok.isUtf32()); + ASSERT_EQUALS(false, tok.isLong()); + ASSERT_EQUALS(false, tok.isCMultiChar()); } void stringTypes() const { diff --git a/test/testutils.cpp b/test/testutils.cpp index 531b2f477ed..a70324fb7a5 100644 --- a/test/testutils.cpp +++ b/test/testutils.cpp @@ -40,6 +40,7 @@ class TestUtils : public TestFixture { TEST_CASE(startsWith); TEST_CASE(trim); TEST_CASE(findAndReplace); + TEST_CASE(replaceEscapeSequences); } void isValidGlobPattern() const { @@ -427,6 +428,16 @@ class TestUtils : public TestFixture { ASSERT_EQUALS("", s); } } + + void replaceEscapeSequences() const { + ASSERT_EQUALS("\x30", ::replaceEscapeSequences("\\x30")); + ASSERT_EQUALS("\030", ::replaceEscapeSequences("\\030")); + ASSERT_EQUALS("\r", ::replaceEscapeSequences("\\r")); + ASSERT_EQUALS("\n", ::replaceEscapeSequences("\\n")); + ASSERT_EQUALS("\t", ::replaceEscapeSequences("\\t")); + ASSERT_EQUALS("\\", ::replaceEscapeSequences("\\\\")); + ASSERT_EQUALS("\"", ::replaceEscapeSequences("\\\"")); + } }; REGISTER_TEST(TestUtils)