Skip to content

Commit

Permalink
Fixup #12929: better handling of escape sequences in Token::isCMultiC…
Browse files Browse the repository at this point in the history
…har (#6597)
  • Loading branch information
danmar authored Jul 13, 2024
1 parent 9b9a4b8 commit ecd73d5
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 26 deletions.
25 changes: 3 additions & 22 deletions lib/token.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 3 additions & 4 deletions lib/token.h
Original file line number Diff line number Diff line change
Expand Up @@ -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?
*
Expand Down
37 changes: 37 additions & 0 deletions lib/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<char>(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<char>(std::stoi(value, nullptr, 8));
} else {
result += source[i];
}
}
}
return result;
}

6 changes: 6 additions & 0 deletions lib/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
8 changes: 8 additions & 0 deletions test/testtoken.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
11 changes: 11 additions & 0 deletions test/testutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class TestUtils : public TestFixture {
TEST_CASE(startsWith);
TEST_CASE(trim);
TEST_CASE(findAndReplace);
TEST_CASE(replaceEscapeSequences);
}

void isValidGlobPattern() const {
Expand Down Expand Up @@ -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)

0 comments on commit ecd73d5

Please sign in to comment.