From 6375fb4f085639ad50dfcf994b50e5b8a84ddbbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Marjam=C3=A4ki?= Date: Sun, 3 Jan 2021 21:03:07 +0100 Subject: [PATCH] New check: Warn about nonportable code that can be optimised (signed integer overflow) --- lib/checktype.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++++ lib/checktype.h | 6 ++++++ test/testtype.cpp | 30 +++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) diff --git a/lib/checktype.cpp b/lib/checktype.cpp index dade64c55..ef17e3593 100644 --- a/lib/checktype.cpp +++ b/lib/checktype.cpp @@ -222,6 +222,55 @@ void CheckType::integerOverflowError(const Token *tok, const ValueFlow::Value &v value.isInconclusive()); } +//--------------------------------------------------------------------------------- +// Checking for code patterns that might be optimised differently by the compilers +//--------------------------------------------------------------------------------- + +void CheckType::checkIntegerOverflowOptimisations() +{ + // Various patterns are defined here: + // https://kristerw.blogspot.com/2016/02/how-undefined-signed-overflow-enables.html + + // These optimisations seem to generate "unwanted" output + // x + c < x -> false + // x + c <= x -> false + // x + c > x -> true + // x + c >= x -> true + + if (!mSettings->isEnabled(Settings::PORTABILITY)) + return; + + for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) { + if (!Token::Match(tok, "<|<=|>=|>") || !tok->isBinaryOp()) + continue; + + if (Token::Match(tok->astOperand1(), "[+-]") && + tok->astOperand1()->valueType() && + tok->astOperand1()->valueType()->isIntegral() && + tok->astOperand1()->valueType()->sign == ValueType::Sign::SIGNED && + tok->astOperand1()->isBinaryOp() && + tok->astOperand1()->astOperand2()->isNumber() && + Token::Match(tok->astOperand1()->astOperand1(), "%var%") && + tok->astOperand1()->astOperand1()->str() == tok->astOperand2()->str()) { + bool result; + if (tok->astOperand1()->str() == "+") + result = Token::Match(tok, ">|>="); + else + result = Token::Match(tok, "<|<="); + integerOverflowOptimisationError(tok, result ? "true" : "false"); + } + } +} + +void CheckType::integerOverflowOptimisationError(const Token *tok, const std::string &replace) +{ + const std::string expr = tok ? tok->expressionString() : "x+c x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+10>x' will be optimised into 'true'. Signed integer overflow is undefined behavior.\n", errout.str()); + + check("int f(int x) { return x + 10 >= x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+10>=x' will be optimised into 'true'. Signed integer overflow is undefined behavior.\n", errout.str()); + + check("int f(int x) { return x + 10 < x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x+10 x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-10>x' will be optimised into 'false'. Signed integer overflow is undefined behavior.\n", errout.str()); + + check("int f(int x) { return x - 10 >= x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-10>=x' will be optimised into 'false'. Signed integer overflow is undefined behavior.\n", errout.str()); + + check("int f(int x) { return x - 10 < x; }", &settings); + ASSERT_EQUALS("[test.cpp:1]: (portability) There is a danger that 'x-10