Set correct type and size of string and char literals (#2275)

* Set correct type and size of string and char literals

Use that string and char literal tokens store the prefix. This makes
it possible to distinghuish between different type of string literals
(i.e., utf8 encoded strings, utf16, wide strings, etc) which have
different type.

When the tokens holding the string and character values have the correct
type, it is possible to improve Token::getStrSize() to give the correct
result for all string types. Previously, it would return the number of
characters in the string, i.e., it would give the wrong size unless
the type of the string was char*.

Since strings now can have different size (in number of bytes) and
length (in number of elements), add a new helper function that returns
the number of characters. Checkers have been updated to use the correct
functions.

Having the size makes it possible to find more problems with prefixed
strings, and to reduce false positives, for example in the buffer
overflow checker.

Also, improve the stringLiteralWrite error message to also print the
prefix of the string (if there is one).

* Add comment and update string length
This commit is contained in:
Rikard Falkeborn 2019-10-20 07:11:57 +02:00 committed by Daniel Marjamäki
parent 00fae7fb42
commit 5c061c1c12
12 changed files with 261 additions and 42 deletions

View File

@ -203,10 +203,10 @@ static bool getDimensionsEtc(const Token * const arrayToken, const Settings *set
return ChildrenToVisit::op1_and_op2;
});
}
} else if (const Token *stringLiteral = array->getValueTokenMinStrSize()) {
} else if (const Token *stringLiteral = array->getValueTokenMinStrSize(settings)) {
Dimension dim;
dim.tok = nullptr;
dim.num = Token::getStrSize(stringLiteral);
dim.num = Token::getStrArraySize(stringLiteral);
dim.known = array->hasKnownValue();
dimensions->emplace_back(dim);
} else if (array->valueType() && array->valueType()->pointer >= 1 && array->valueType()->isIntegral()) {

View File

@ -60,7 +60,7 @@ void CheckString::stringLiteralWrite()
for (const Token* tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) {
if (!tok->variable() || !tok->variable()->isPointer())
continue;
const Token *str = tok->getValueTokenMinStrSize();
const Token *str = tok->getValueTokenMinStrSize(mSettings);
if (!str)
continue;
if (Token::Match(tok, "%var% [") && Token::simpleMatch(tok->linkAt(1), "] ="))
@ -80,10 +80,11 @@ void CheckString::stringLiteralWriteError(const Token *tok, const Token *strValu
std::string errmsg("Modifying string literal");
if (strValue) {
std::string s = strValue->strValue();
if (s.size() > 15U)
s = s.substr(0,13) + "..";
errmsg += " \"" + s + "\"";
std::string s = strValue->str();
// 20 is an arbitrary value, the max string length shown in a warning message
if (s.size() > 20U)
s = s.substr(0,17) + "..\"";
errmsg += " " + s;
}
errmsg += " directly or indirectly is undefined behaviour.";

View File

@ -5593,13 +5593,26 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings)
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U));
} else if (tok->isBoolean()) {
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::BOOL, 0U));
} else if (tok->tokType() == Token::eChar) {
setValueType(tok, ValueType(ValueType::Sign::UNKNOWN_SIGN, tok->isLong() ? ValueType::Type::WCHAR_T : ValueType::Type::CHAR, 0U));
} else if (tok->tokType() == Token::eString) {
ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, 1U, 1U);
if (tok->isLong()) {
} else if (tok->tokType() == Token::eChar || tok->tokType() == Token::eString) {
nonneg int pointer = tok->tokType() == Token::eChar ? 0U : 1U;
nonneg int constness = tok->tokType() == Token::eChar ? 0U : 1U;
ValueType valuetype(ValueType::Sign::UNKNOWN_SIGN, ValueType::Type::CHAR, pointer, constness);
if (mIsCpp && mSettings->standards.cpp >= Standards::CPP20 && tok->isUtf8()) {
valuetype.originalTypeName = "char8_t";
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
} else if (tok->isUtf16()) {
valuetype.originalTypeName = "char16_t";
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
} else if (tok->isUtf32()) {
valuetype.originalTypeName = "char32_t";
valuetype.fromLibraryType(valuetype.originalTypeName, mSettings);
} else if (tok->isLong()) {
valuetype.originalTypeName = "wchar_t";
valuetype.type = ValueType::Type::WCHAR_T;
} else if ((tok->tokType() == Token::eChar) && ((tok->isCChar() && !mIsCpp) || (tok->isCMultiChar()))) {
valuetype.type = ValueType::Type::INT;
valuetype.sign = ValueType::Sign::SIGNED;
}
setValueType(tok, valuetype);
} else if (tok->str() == "(") {

View File

@ -722,7 +722,7 @@ nonneg int Token::getStrLength(const Token *tok)
return len;
}
nonneg int Token::getStrSize(const Token *tok)
nonneg int Token::getStrArraySize(const Token *tok)
{
assert(tok != nullptr);
assert(tok->tokType() == eString);
@ -736,6 +736,18 @@ nonneg int Token::getStrSize(const Token *tok)
return sizeofstring;
}
nonneg int Token::getStrSize(const Token *tok, const Settings *settings)
{
assert(tok != nullptr && tok->tokType() == eString);
nonneg int sizeofType = 1;
if (tok->valueType()) {
ValueType vt(*tok->valueType());
vt.pointer = 0;
sizeofType = ValueFlow::getSizeOf(vt, settings);
}
return getStrArraySize(tok) * sizeofType;
}
std::string Token::getCharAt(const Token *tok, MathLib::bigint index)
{
assert(tok != nullptr);
@ -1713,7 +1725,7 @@ const ValueFlow::Value * Token::getInvalidValue(const Token *ftok, nonneg int ar
return ret;
}
const Token *Token::getValueTokenMinStrSize() const
const Token *Token::getValueTokenMinStrSize(const Settings *settings) const
{
if (!mImpl->mValues)
return nullptr;
@ -1722,7 +1734,7 @@ const Token *Token::getValueTokenMinStrSize() const
std::list<ValueFlow::Value>::const_iterator it;
for (it = mImpl->mValues->begin(); it != mImpl->mValues->end(); ++it) {
if (it->isTokValue() && it->tokvalue && it->tokvalue->tokType() == Token::eString) {
const int size = getStrSize(it->tokvalue);
const int size = getStrSize(it->tokvalue, settings);
if (!ret || size < minsize) {
minsize = size;
ret = it->tokvalue;

View File

@ -318,13 +318,23 @@ public:
static nonneg int getStrLength(const Token *tok);
/**
* @return sizeof of C-string.
* @return array length of C-string.
*
* Should be called for %%str%% tokens only.
*
* @param tok token with C-string
**/
static nonneg int getStrSize(const Token *tok);
static nonneg int getStrArraySize(const Token *tok);
/**
* @return sizeof of C-string.
*
* Should be called for %%str%% tokens only.
*
* @param tok token with C-string
* @param settings Settings
**/
static nonneg int getStrSize(const Token *tok, const Settings *const);
/**
* @return char of C-string at index (possible escaped "\\n")
@ -602,6 +612,30 @@ public:
mImpl->mBits = b;
}
bool isUtf8() const {
return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u8")) ||
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u8")));
}
bool isUtf16() const {
return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "u")) ||
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "u")));
}
bool isUtf32() const {
return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "U")) ||
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "U")));
}
bool isCChar() const {
return (((mTokType == eString) && isPrefixStringCharLiteral(mStr, '"', "")) ||
((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "") && mStr.length() == 3));
}
bool isCMultiChar() const {
return (((mTokType == eChar) && isPrefixStringCharLiteral(mStr, '\'', "")) &&
(mStr.length() > 3));
}
/**
* @brief Is current token a template argument?
*
@ -1042,7 +1076,7 @@ public:
}
const Token *getValueTokenMaxStrLength() const;
const Token *getValueTokenMinStrSize() const;
const Token *getValueTokenMinStrSize(const Settings *settings) const;
const Token *getValueTokenDeadPointer() const;

View File

@ -2679,7 +2679,7 @@ void Tokenizer::arraySize()
if (addlength || Token::Match(tok, "%var% [ ] = %str% ;")) {
tok = tok->next();
const int sz = Token::getStrSize(tok->tokAt(3));
const int sz = Token::getStrArraySize(tok->tokAt(3));
tok->insertToken(MathLib::toString(sz));
tok = tok->tokAt(5);
}

View File

@ -1094,7 +1094,7 @@ static nonneg int getSizeOfType(const Token *typeTok, const Settings *settings)
return 0;
}
static size_t getSizeOf(const ValueType &vt, const Settings *settings)
size_t ValueFlow::getSizeOf(const ValueType &vt, const Settings *settings)
{
if (vt.pointer)
return settings->sizeof_pointer;
@ -1154,7 +1154,7 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b
}
if (Token::simpleMatch(tok, "sizeof ( *")) {
const ValueType *vt = tok->tokAt(2)->valueType();
const size_t sz = vt ? getSizeOf(*vt, settings) : 0;
const size_t sz = vt ? ValueFlow::getSizeOf(*vt, settings) : 0;
if (sz > 0) {
ValueFlow::Value value(sz);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
@ -1212,7 +1212,7 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b
if (var->type()->classScope && var->type()->classScope->enumType)
size = getSizeOfType(var->type()->classScope->enumType, settings);
} else if (var->valueType()) {
size = getSizeOf(*var->valueType(), settings);
size = ValueFlow::getSizeOf(*var->valueType(), settings);
} else if (!var->type()) {
size = getSizeOfType(var->typeStartToken(), settings);
}
@ -1234,7 +1234,7 @@ static Token * valueFlowSetConstantValue(Token *tok, const Settings *settings, b
}
} else if (!tok2->type()) {
const ValueType &vt = ValueType::parseDecl(tok2,settings);
const size_t sz = getSizeOf(vt, settings);
const size_t sz = ValueFlow::getSizeOf(vt, settings);
if (sz > 0) {
ValueFlow::Value value(sz);
if (!tok2->isTemplateArg() && settings->platformType != cppcheck::Platform::Unspecified)
@ -4289,7 +4289,7 @@ static std::list<ValueFlow::Value> truncateValues(std::list<ValueFlow::Value> va
if (!valueType || !valueType->isIntegral())
return values;
const size_t sz = getSizeOf(*valueType, settings);
const size_t sz = ValueFlow::getSizeOf(*valueType, settings);
for (ValueFlow::Value &value : values) {
if (value.isFloatValue()) {

View File

@ -34,6 +34,7 @@ class Settings;
class SymbolDatabase;
class Token;
class TokenList;
class ValueType;
class Variable;
namespace ValueFlow {
@ -320,6 +321,8 @@ namespace ValueFlow {
void setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings);
std::string eitherTheConditionIsRedundant(const Token *condition);
size_t getSizeOf(const ValueType &vt, const Settings *settings);
}
struct LifetimeToken {

View File

@ -3198,6 +3198,7 @@ private:
doc.Parse(xmldata, sizeof(xmldata));
settings.library.load(doc);
settings.addEnabled("warning");
settings.sizeof_wchar_t = 4;
check("void f() {\n"
" char c[10];\n"
@ -3236,10 +3237,28 @@ private:
// Ticket #909
check("void f(void) {\n"
" char str[] = \"abcd\";\n"
" mymemset(str, 0, 10);\n"
" mymemset(str, 0, 6);\n"
"}", settings);
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
check("void f(void) {\n"
" char str[] = \"abcd\";\n"
" mymemset(str, 0, 5);\n"
"}", settings);
ASSERT_EQUALS("", errout.str());
check("void f(void) {\n"
" wchar_t str[] = L\"abcd\";\n"
" mymemset(str, 0, 21);\n"
"}", settings);
ASSERT_EQUALS("[test.cpp:3]: (error) Buffer is accessed out of bounds: str\n", errout.str());
check("void f(void) {\n"
" wchar_t str[] = L\"abcd\";\n"
" mymemset(str, 0, 20);\n"
"}", settings);
ASSERT_EQUALS("", errout.str());
// ticket #1659 - overflowing variable when using memcpy
check("void f(void) { \n"
" char c;\n"

View File

@ -88,6 +88,12 @@ private:
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"abc\" directly or indirectly is undefined behaviour.\n", errout.str());
check("void f() {\n"
" char *abc = \"A very long string literal\";\n"
" abc[0] = 'a';\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"A very long stri..\" directly or indirectly is undefined behaviour.\n", errout.str());
check("void f() {\n"
" QString abc = \"abc\";\n"
" abc[0] = 'a';\n"
@ -118,13 +124,13 @@ private:
" wchar_t *abc = L\"abc\";\n"
" abc[0] = u'a';\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"abc\" directly or indirectly is undefined behaviour.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal L\"abc\" directly or indirectly is undefined behaviour.\n", errout.str());
check("void f() {\n"
" char16_t *abc = u\"abc\";\n"
" abc[0] = 'a';\n"
"}");
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal \"abc\" directly or indirectly is undefined behaviour.\n", errout.str());
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:2]: (error) Modifying string literal u\"abc\" directly or indirectly is undefined behaviour.\n", errout.str());
}
void alwaysTrueFalseStringCompare() {

View File

@ -5361,6 +5361,7 @@ private:
GET_SYMBOL_DB("void foo1(int, char* a) { }\n"
"void foo1(int, char a) { }\n"
"void foo1(int, wchar_t a) { }\n"
"void foo1(int, char16_t a) { }\n"
"void foo2(int, float a) { }\n"
"void foo2(int, wchar_t a) { }\n"
"void foo3(int, float a) { }\n"
@ -5368,10 +5369,11 @@ private:
"void func() {\n"
" foo1(1, 'c');\n"
" foo1(2, L'c');\n"
" foo2(3, 'c');\n"
" foo2(4, L'c');\n"
" foo3(5, 'c');\n"
" foo3(6, L'c');\n"
" foo1(3, u'c');\n"
" foo2(4, 'c');\n"
" foo2(5, L'c');\n"
" foo3(6, 'c');\n"
" foo3(7, L'c');\n"
"}");
ASSERT_EQUALS("", errout.str());
@ -5382,18 +5384,21 @@ private:
f = Token::findsimplematch(tokenizer.tokens(), "foo1 ( 2");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 3);
f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 3");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5);
f = Token::findsimplematch(tokenizer.tokens(), "foo1 ( 3");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 4);
f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 4");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 5);
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6);
f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 5");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7);
f = Token::findsimplematch(tokenizer.tokens(), "foo2 ( 5");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 6);
f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 6");
ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8);
// Error: ambiguous function call
//f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 6");
//ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 7);
//f = Token::findsimplematch(tokenizer.tokens(), "foo3 ( 7");
//ASSERT_EQUALS(true, db && f && f->function() && f->function()->tokenDef->linenr() == 8);
}
void findFunction16() {
@ -6369,6 +6374,14 @@ private:
ASSERT_EQUALS("signed long", typeOf("(signed long)1 + (unsigned int)2;", "+"));
ASSERT_EQUALS("unsigned long", typeOf("(unsigned long)1 + (signed int)2;", "+"));
// char
ASSERT_EQUALS("char", typeOf("'a';", "'a'", "test.cpp"));
ASSERT_EQUALS("signed int", typeOf("'a';", "'a'", "test.c"));
ASSERT_EQUALS("wchar_t", typeOf("L'a';", "L'a'", "test.cpp"));
ASSERT_EQUALS("wchar_t", typeOf("L'a';", "L'a'", "test.c"));
ASSERT_EQUALS("signed int", typeOf("'aaa';", "'aaa'", "test.cpp"));
ASSERT_EQUALS("signed int", typeOf("'aaa';", "'aaa'", "test.c"));
// char *
ASSERT_EQUALS("const char *", typeOf("\"hello\" + 1;", "+"));
ASSERT_EQUALS("const char", typeOf("\"hello\"[1];", "["));
@ -6498,6 +6511,25 @@ private:
ASSERT_EQUALS("unsigned long long", typeOf("enum E : unsigned long long { }; void foo() { E e[3]; bar(e[0]); }", "[ 0"));
// Library types
{
// Char types
Settings settings;
const Library::PodType char8 = { 1, 'u' };
const Library::PodType char16 = { 2, 'u' };
const Library::PodType char32 = { 4, 'u' };
settings.library.mPodTypes["char8_t"] = char8;
settings.library.mPodTypes["char16_t"] = char16;
settings.library.mPodTypes["char32_t"] = char32;
settings.sizeof_short = 2;
settings.sizeof_int = 4;
ASSERT_EQUALS("unsigned char", typeOf("u8'a';", "u8'a'", "test.cpp", &settings));
ASSERT_EQUALS("unsigned short", typeOf("u'a';", "u'a'", "test.cpp", &settings));
ASSERT_EQUALS("unsigned int", typeOf("U'a';", "U'a'", "test.cpp", &settings));
ASSERT_EQUALS("const unsigned char *", typeOf("u8\"a\";", "u8\"a\"", "test.cpp", &settings));
ASSERT_EQUALS("const unsigned short *", typeOf("u\"a\";", "u\"a\"", "test.cpp", &settings));
ASSERT_EQUALS("const unsigned int *", typeOf("U\"a\";", "U\"a\"", "test.cpp", &settings));
}
{
// PodType
Settings settingsWin64;

View File

@ -56,6 +56,8 @@ private:
TEST_CASE(multiCompare3); // false positive for %or% on code using "|="
TEST_CASE(multiCompare4);
TEST_CASE(multiCompare5);
TEST_CASE(charTypes);
TEST_CASE(stringTypes);
TEST_CASE(getStrLength);
TEST_CASE(getStrSize);
TEST_CASE(getCharAt);
@ -266,6 +268,102 @@ private:
ASSERT_EQUALS(true, Token::multiCompare(&tok, "+|%or%|%oror%", 0) >= 0);
}
void charTypes() const {
Token tok;
tok.str("'a'");
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());
tok.str("u8'a'");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(true, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("u'a'");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(true, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("U'a'");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(true, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("L'a'");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(true, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("'aaa'");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(true, tok.isCMultiChar());
}
void stringTypes() const {
Token tok;
tok.str("\"a\"");
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());
tok.str("u8\"a\"");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(true, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("u\"a\"");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(true, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("U\"a\"");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(true, tok.isUtf32());
ASSERT_EQUALS(false, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
tok.str("L\"a\"");
ASSERT_EQUALS(false, tok.isCChar());
ASSERT_EQUALS(false, tok.isUtf8());
ASSERT_EQUALS(false, tok.isUtf16());
ASSERT_EQUALS(false, tok.isUtf32());
ASSERT_EQUALS(true, tok.isLong());
ASSERT_EQUALS(false, tok.isCMultiChar());
}
void getStrLength() const {
Token tok;
@ -296,18 +394,19 @@ private:
void getStrSize() const {
Token tok;
Settings settings;
tok.str("\"\"");
ASSERT_EQUALS(sizeof(""), Token::getStrSize(&tok));
ASSERT_EQUALS(sizeof(""), Token::getStrSize(&tok, &settings));
tok.str("\"abc\"");
ASSERT_EQUALS(sizeof("abc"), Token::getStrSize(&tok));
ASSERT_EQUALS(sizeof("abc"), Token::getStrSize(&tok, &settings));
tok.str("\"\\0abc\"");
ASSERT_EQUALS(sizeof("\0abc"), Token::getStrSize(&tok));
ASSERT_EQUALS(sizeof("\0abc"), Token::getStrSize(&tok, &settings));
tok.str("\"\\\\\"");
ASSERT_EQUALS(sizeof("\\"), Token::getStrSize(&tok));
ASSERT_EQUALS(sizeof("\\"), Token::getStrSize(&tok, &settings));
}
void getCharAt() const {