Skip to content

Commit ecd73d5

Browse files
authored
Fixup #12929: better handling of escape sequences in Token::isCMultiChar (#6597)
1 parent 9b9a4b8 commit ecd73d5

6 files changed

Lines changed: 68 additions & 26 deletions

File tree

lib/token.cpp

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -729,29 +729,10 @@ nonneg int Token::getStrLength(const Token *tok)
729729
assert(tok != nullptr);
730730
assert(tok->mTokType == eString);
731731

732-
int len = 0;
733-
// cppcheck-suppress shadowFunction - TODO: fix this
734-
const std::string str(getStringLiteral(tok->str()));
735-
std::string::const_iterator it = str.cbegin();
736-
const std::string::const_iterator end = str.cend();
737-
738-
while (it != end) {
739-
if (*it == '\\') {
740-
++it;
741-
742-
// string ends at '\0'
743-
if (*it == '0')
744-
return len;
745-
}
746-
747-
if (*it == '\0')
748-
return len;
749-
750-
++it;
751-
++len;
752-
}
732+
const std::string s(replaceEscapeSequences(getStringLiteral(tok->str())));
753733

754-
return len;
734+
const auto pos = s.find('\0');
735+
return pos < s.size() ? pos : s.size();
755736
}
756737

757738
nonneg int Token::getStrArraySize(const Token *tok)

lib/token.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -751,14 +751,13 @@ class CPPCHECKLIB Token {
751751

752752
bool isCChar() const {
753753
return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', emptyString)) ||
754-
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && mStr.length() == 3) ||
755-
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && mStr.compare(0, 2, "\'\\") == 0 && mStr.length() == 4));
754+
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && (replaceEscapeSequences(getCharLiteral(mStr)).size() == 1)));
756755
}
757756

758757
bool isCMultiChar() const {
759-
return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString)) && (mStr.compare(0, 2, "\'\\") != 0 || mStr.length() > 4) &&
760-
(mStr.length() > 3));
758+
return (mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', emptyString) && (replaceEscapeSequences(getCharLiteral(mStr)).size() > 1);
761759
}
760+
762761
/**
763762
* @brief Is current token a template argument?
764763
*

lib/utils.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,40 @@ void findAndReplace(std::string &source, const std::string &searchFor, const std
147147
index += replaceWith.length();
148148
}
149149
}
150+
151+
std::string replaceEscapeSequences(const std::string &source) {
152+
std::string result;
153+
result.reserve(source.size());
154+
for (std::size_t i = 0; i < source.size(); ++i) {
155+
if (source[i] != '\\' || i + 1 >= source.size())
156+
result += source[i];
157+
else {
158+
++i;
159+
if (source[i] == 'n') {
160+
result += '\n';
161+
} else if (source[i] == 'r') {
162+
result += '\r';
163+
} else if (source[i] == 't') {
164+
result += '\t';
165+
} else if (source[i] == 'x') {
166+
std::string value = "0";
167+
if (i + 1 < source.size() && std::isxdigit(source[i+1]))
168+
value += source[i++ + 1];
169+
if (i + 1 < source.size() && std::isxdigit(source[i+1]))
170+
value += source[i++ + 1];
171+
result += static_cast<char>(std::stoi(value, nullptr, 16));
172+
} else if (source[i] == '0') {
173+
std::string value = "0";
174+
if (i + 1 < source.size() && source[i+1] >= '0' && source[i+1] <= '7')
175+
value += source[i++ + 1];
176+
if (i + 1 < source.size() && source[i+1] >= '0' && source[i+1] <= '7')
177+
value += source[i++ + 1];
178+
result += static_cast<char>(std::stoi(value, nullptr, 8));
179+
} else {
180+
result += source[i];
181+
}
182+
}
183+
}
184+
return result;
185+
}
186+

lib/utils.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,12 @@ CPPCHECKLIB std::string trim(const std::string& s, const std::string& t = " \t")
364364
*/
365365
CPPCHECKLIB void findAndReplace(std::string &source, const std::string &searchFor, const std::string &replaceWith);
366366

367+
/**
368+
* Replace all escape sequences in the given string.
369+
* @param source The string that contains escape sequences
370+
*/
371+
CPPCHECKLIB std::string replaceEscapeSequences(const std::string &source);
372+
367373
namespace cppcheck
368374
{
369375
NORETURN inline void unreachable()

test/testtoken.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,14 @@ class TestToken : public TestFixture {
371371
ASSERT_EQUALS(false, tok.isUtf32());
372372
ASSERT_EQUALS(false, tok.isLong());
373373
ASSERT_EQUALS(true, tok.isCMultiChar());
374+
375+
tok.str("'\\x10'");
376+
ASSERT_EQUALS(true, tok.isCChar());
377+
ASSERT_EQUALS(false, tok.isUtf8());
378+
ASSERT_EQUALS(false, tok.isUtf16());
379+
ASSERT_EQUALS(false, tok.isUtf32());
380+
ASSERT_EQUALS(false, tok.isLong());
381+
ASSERT_EQUALS(false, tok.isCMultiChar());
374382
}
375383

376384
void stringTypes() const {

test/testutils.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class TestUtils : public TestFixture {
4040
TEST_CASE(startsWith);
4141
TEST_CASE(trim);
4242
TEST_CASE(findAndReplace);
43+
TEST_CASE(replaceEscapeSequences);
4344
}
4445

4546
void isValidGlobPattern() const {
@@ -427,6 +428,16 @@ class TestUtils : public TestFixture {
427428
ASSERT_EQUALS("", s);
428429
}
429430
}
431+
432+
void replaceEscapeSequences() const {
433+
ASSERT_EQUALS("\x30", ::replaceEscapeSequences("\\x30"));
434+
ASSERT_EQUALS("\030", ::replaceEscapeSequences("\\030"));
435+
ASSERT_EQUALS("\r", ::replaceEscapeSequences("\\r"));
436+
ASSERT_EQUALS("\n", ::replaceEscapeSequences("\\n"));
437+
ASSERT_EQUALS("\t", ::replaceEscapeSequences("\\t"));
438+
ASSERT_EQUALS("\\", ::replaceEscapeSequences("\\\\"));
439+
ASSERT_EQUALS("\"", ::replaceEscapeSequences("\\\""));
440+
}
430441
};
431442

432443
REGISTER_TEST(TestUtils)

0 commit comments

Comments
 (0)