Fix severity of c++14 shifts with too many bits (#2213)
For c++14, shifting a variable with a value larger than or equal to the number of bits in the variable is undefined. Left-shifting with a value equal to the number of bits of the variable is implementation defined. See also trac ticket #9306.
This commit is contained in:
parent
9e76630a4b
commit
4dbf006dc7
|
@ -57,11 +57,6 @@ void CheckType::checkTooBigBitwiseShift()
|
|||
if (mSettings->platformType == Settings::Unspecified)
|
||||
return;
|
||||
|
||||
const bool cpp14 = mSettings->standards.cpp >= Standards::CPP14;
|
||||
|
||||
if (cpp14 && !mSettings->isEnabled(Settings::PORTABILITY))
|
||||
return;
|
||||
|
||||
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
||||
// C++ and macro: OUT(x<<y)
|
||||
if (mTokenizer->isCPP() && Token::Match(tok, "[;{}] %name% (") && Token::simpleMatch(tok->linkAt(2), ") ;") && tok->next()->isUpperCaseName() && !tok->next()->function())
|
||||
|
@ -96,7 +91,7 @@ void CheckType::checkTooBigBitwiseShift()
|
|||
|
||||
// Get biggest rhs value. preferably a value which doesn't have 'condition'.
|
||||
const ValueFlow::Value * value = tok->astOperand2()->getValueGE(lhsbits, mSettings);
|
||||
if (value && mSettings->isEnabled(value, false) && !cpp14)
|
||||
if (value && mSettings->isEnabled(value, false))
|
||||
tooBigBitwiseShiftError(tok, lhsbits, *value);
|
||||
else if (lhstype->sign == ValueType::Sign::SIGNED) {
|
||||
value = tok->astOperand2()->getValueGE(lhsbits-1, mSettings);
|
||||
|
@ -150,6 +145,8 @@ void CheckType::tooBigSignedBitwiseShiftError(const Token *tok, int lhsbits, con
|
|||
if (cpp14)
|
||||
severity = Severity::portability;
|
||||
|
||||
if ((severity == Severity::portability) && !mSettings->isEnabled(Settings::PORTABILITY))
|
||||
return;
|
||||
reportError(errorPath, severity, id, errmsg.str(), CWE758, rhsbits.isInconclusive());
|
||||
}
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ private:
|
|||
TEST_CASE(checkFloatToIntegerOverflow);
|
||||
}
|
||||
|
||||
void check(const char code[], Settings* settings = nullptr, const char filename[] = "test.cpp") {
|
||||
void check(const char code[], Settings* settings = nullptr, const char filename[] = "test.cpp", const std::string& standard = "c++11") {
|
||||
// Clear the error buffer..
|
||||
errout.str("");
|
||||
|
||||
|
@ -50,7 +50,8 @@ private:
|
|||
settings = &_settings;
|
||||
}
|
||||
settings->addEnabled("warning");
|
||||
settings->standards.setCPP("c++11");
|
||||
settings->addEnabled("portability");
|
||||
settings->standards.setCPP(standard);
|
||||
|
||||
// Tokenize..
|
||||
Tokenizer tokenizer(settings, this);
|
||||
|
@ -71,6 +72,8 @@ private:
|
|||
{
|
||||
const std::string types[] = {"unsigned char", /*[unsigned]*/"char", "bool", "unsigned short", "unsigned int", "unsigned long"};
|
||||
for (const std::string& type : types) {
|
||||
check((type + " f(" + type +" x) { return x << 31; }").c_str(), &settings);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
check((type + " f(" + type +" x) { return x << 33; }").c_str(), &settings);
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 33 bits is undefined behaviour\n", errout.str());
|
||||
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings);
|
||||
|
@ -84,6 +87,7 @@ private:
|
|||
{
|
||||
const std::string types[] = {"signed char", "signed short", /*[signed]*/"short", "wchar_t", /*[signed]*/"int", "signed int", /*[signed]*/"long", "signed long"};
|
||||
for (const std::string& type : types) {
|
||||
// c++11
|
||||
check((type + " f(" + type +" x) { return x << 33; }").c_str(), &settings);
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 33 bits is undefined behaviour\n", errout.str());
|
||||
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings);
|
||||
|
@ -92,6 +96,12 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting signed 32-bit value by 31 bits is undefined behaviour\n", errout.str());
|
||||
check((type + " foo(" + type + " x) { return x << 30; }").c_str(), &settings);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// c++14
|
||||
check((type + " foo(" + type + " x) { return x << 31; }").c_str(), &settings, "test.cpp", "c++14");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting signed 32-bit value by 31 bits is implementation-defined behaviour\n", errout.str());
|
||||
check((type + " f(int x) { return (x = (" + type + ")x << 32); }").c_str(), &settings, "test.cpp", "c++14");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 32-bit value by 32 bits is undefined behaviour\n", errout.str());
|
||||
}
|
||||
}
|
||||
// 64 bit width types
|
||||
|
@ -121,6 +131,16 @@ private:
|
|||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting signed 64-bit value by 63 bits is undefined behaviour\n", errout.str());
|
||||
check("signed long long f(signed long long x) { return x << 62; }",&settings);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// c++14
|
||||
check("signed long long foo(signed long long x) { return x << 64; }",&settings, "test.cpp", "c++14");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 64-bit value by 64 bits is undefined behaviour\n", errout.str());
|
||||
check("signed long long f(long long x) { return (x = (signed long long)x << 64); }",&settings, "test.cpp", "c++14");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (error) Shifting 64-bit value by 64 bits is undefined behaviour\n", errout.str());
|
||||
check("signed long long f(signed long long x) { return x << 63; }",&settings, "test.cpp", "c++14");
|
||||
ASSERT_EQUALS("[test.cpp:1]: (portability) Shifting signed 64-bit value by 63 bits is implementation-defined behaviour\n", errout.str());
|
||||
check("signed long long f(signed long long x) { return x << 62; }",&settings);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
check("void foo() {\n"
|
||||
|
|
Loading…
Reference in New Issue