Extend constStatement checker
This reworks constStatement to find more issues. It catches issue [8827](https://trac.cppcheck.net/ticket/8827): ```cpp extern void foo(int,const char*,int); void f(int value) { foo(42,"test",42),(value&42); } ``` It also catches from issue [8451](https://trac.cppcheck.net/ticket/8451): ```cpp void f1(int x) { 1; (1); (char)1; ((char)1); !x; (!x); ~x; } ``` And also: ```cpp void f(int x) { x; } ``` The other examples are not caught due to incomplete AST.
This commit is contained in:
parent
dc4e7cef88
commit
cf1ad5087a
|
@ -734,57 +734,6 @@ void CheckOther::suspiciousCaseInSwitchError(const Token* tok, const std::string
|
||||||
"Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, true);
|
"Using an operator like '" + operatorString + "' in a case label is suspicious. Did you intend to use a bitwise operator, multiple case labels or if/else instead?", CWE398, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
// if (x == 1)
|
|
||||||
// x == 0; // <- suspicious equality comparison.
|
|
||||||
//---------------------------------------------------------------------------
|
|
||||||
void CheckOther::checkSuspiciousEqualityComparison()
|
|
||||||
{
|
|
||||||
if (!mSettings->isEnabled(Settings::WARNING) || !mSettings->inconclusive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const SymbolDatabase* symbolDatabase = mTokenizer->getSymbolDatabase();
|
|
||||||
for (const Scope * scope : symbolDatabase->functionScopes) {
|
|
||||||
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
|
||||||
if (Token::simpleMatch(tok, "for (")) {
|
|
||||||
const Token* const openParen = tok->next();
|
|
||||||
const Token* const closeParen = tok->linkAt(1);
|
|
||||||
|
|
||||||
// Search for any suspicious equality comparison in the initialization
|
|
||||||
// or increment-decrement parts of the for() loop.
|
|
||||||
// For example:
|
|
||||||
// for (i == 2; i < 10; i++)
|
|
||||||
// or
|
|
||||||
// for (i = 0; i < 10; i == a)
|
|
||||||
if (Token::Match(openParen->next(), "%name% =="))
|
|
||||||
suspiciousEqualityComparisonError(openParen->tokAt(2));
|
|
||||||
if (closeParen->strAt(-2) == "==")
|
|
||||||
suspiciousEqualityComparisonError(closeParen->tokAt(-2));
|
|
||||||
|
|
||||||
// Skip over for() loop conditions because "for (;running==1;)"
|
|
||||||
// is a bit strange, but not necessarily incorrect.
|
|
||||||
tok = closeParen;
|
|
||||||
} else if (Token::Match(tok, "[;{}] *| %name% == %any% ;")) {
|
|
||||||
|
|
||||||
// Exclude compound statements surrounded by parentheses, such as
|
|
||||||
// printf("%i\n", ({x==0;}));
|
|
||||||
// because they may appear as an expression in GNU C/C++.
|
|
||||||
// See http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html
|
|
||||||
const Token* afterStatement = tok->strAt(1) == "*" ? tok->tokAt(6) : tok->tokAt(5);
|
|
||||||
if (!Token::simpleMatch(afterStatement, "} )"))
|
|
||||||
suspiciousEqualityComparisonError(tok->next());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CheckOther::suspiciousEqualityComparisonError(const Token* tok)
|
|
||||||
{
|
|
||||||
reportError(tok, Severity::warning, "suspiciousEqualityComparison",
|
|
||||||
"Found suspicious equality comparison. Did you intend to assign a value instead?", CWE482, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Find consecutive return, break, continue, goto or throw statements. e.g.:
|
// Find consecutive return, break, continue, goto or throw statements. e.g.:
|
||||||
// break; break;
|
// break; break;
|
||||||
|
@ -1389,64 +1338,106 @@ void CheckOther::charBitOpError(const Token *tok)
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
// Incomplete statement..
|
// Incomplete statement..
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
static bool isConstStatement(const Token *tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
if (tok->isExpandedMacro())
|
||||||
|
return false;
|
||||||
|
if (Token::Match(tok, "%bool%|%num%|%str%|%char%|nullptr|NULL"))
|
||||||
|
return true;
|
||||||
|
if (Token::Match(tok, "%var%"))
|
||||||
|
return true;
|
||||||
|
if (Token::Match(tok, "*|&|&&") &&
|
||||||
|
(Token::Match(tok->previous(), "::|.") || Token::Match(tok->astOperand1(), "%type%") ||
|
||||||
|
Token::Match(tok->astOperand2(), "%type%")))
|
||||||
|
return false;
|
||||||
|
if (Token::Match(tok, "<<|>>") && !astIsIntegral(tok, false))
|
||||||
|
return false;
|
||||||
|
if (Token::Match(tok, "!|~|%cop%") && (tok->astOperand1() || tok->astOperand2()))
|
||||||
|
return true;
|
||||||
|
if (Token::simpleMatch(tok->previous(), "sizeof ("))
|
||||||
|
return true;
|
||||||
|
if (isCPPCast(tok))
|
||||||
|
return isConstStatement(tok->astOperand2());
|
||||||
|
if (Token::Match(tok, "( %type%"))
|
||||||
|
return isConstStatement(tok->astOperand1());
|
||||||
|
if (Token::simpleMatch(tok, ","))
|
||||||
|
return isConstStatement(tok->astOperand2());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isVoidStmt(const Token *tok)
|
||||||
|
{
|
||||||
|
if (Token::simpleMatch(tok, "( void"))
|
||||||
|
return true;
|
||||||
|
const Token *tok2 = tok;
|
||||||
|
while (tok2->astOperand1())
|
||||||
|
tok2 = tok2->astOperand1();
|
||||||
|
if (Token::simpleMatch(tok2->previous(), ")") && Token::simpleMatch(tok2->previous()->link(), "( void"))
|
||||||
|
return true;
|
||||||
|
if (Token::simpleMatch(tok2, "( void"))
|
||||||
|
return true;
|
||||||
|
return Token::Match(tok2->previous(), "delete|throw|return");
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isConstTop(const Token *tok)
|
||||||
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
|
if (tok == tok->astTop())
|
||||||
|
return true;
|
||||||
|
if (Token::simpleMatch(tok->astParent(), ";") && tok->astTop() &&
|
||||||
|
Token::Match(tok->astTop()->previous(), "for|if (") && Token::simpleMatch(tok->astTop()->astOperand2(), ";")) {
|
||||||
|
if (Token::simpleMatch(tok->astParent()->astParent(), ";"))
|
||||||
|
return tok->astParent()->astOperand2() == tok;
|
||||||
|
else
|
||||||
|
return tok->astParent()->astOperand1() == tok;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void CheckOther::checkIncompleteStatement()
|
void CheckOther::checkIncompleteStatement()
|
||||||
{
|
{
|
||||||
if (!mSettings->isEnabled(Settings::WARNING))
|
if (!mSettings->isEnabled(Settings::WARNING))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
for (const Token *tok = mTokenizer->tokens(); tok; tok = tok->next()) {
|
||||||
if (Token::Match(tok, "(|["))
|
const Scope *scope = tok->scope();
|
||||||
tok = tok->link();
|
if (scope && !scope->isExecutable())
|
||||||
|
|
||||||
else if (tok->str() == "{" && tok->astParent())
|
|
||||||
tok = tok->link();
|
|
||||||
|
|
||||||
// C++11 struct/array/etc initialization in initializer list
|
|
||||||
else if (Token::Match(tok->previous(), "%var%|] {"))
|
|
||||||
tok = tok->link();
|
|
||||||
|
|
||||||
if (!Token::Match(tok, "[;{}] %str%|%num%"))
|
|
||||||
continue;
|
continue;
|
||||||
|
if (!isConstTop(tok))
|
||||||
// No warning if numeric constant is followed by a "." or ","
|
|
||||||
if (Token::Match(tok->next(), "%num% [,.]"))
|
|
||||||
continue;
|
continue;
|
||||||
|
const Token *rtok = nextAfterAstRightmostLeaf(tok);
|
||||||
// No warning for [;{}] (void *) 0 ;
|
if (!Token::simpleMatch(tok->astParent(), ";") && !Token::simpleMatch(rtok, ";") &&
|
||||||
if (Token::Match(tok, "[;{}] 0 ;") && (tok->next()->isCast() || tok->next()->isExpandedMacro()))
|
!Token::Match(tok->previous(), ";|}|{ %any% ;"))
|
||||||
continue;
|
continue;
|
||||||
|
// Skipe statement expressions
|
||||||
// bailout if there is a "? :" in this statement
|
if (Token::simpleMatch(rtok, "; } )"))
|
||||||
bool bailout = false;
|
|
||||||
for (const Token *tok2 = tok->tokAt(2); tok2; tok2 = tok2->next()) {
|
|
||||||
if (tok2->str() == "?") {
|
|
||||||
bailout = true;
|
|
||||||
break;
|
|
||||||
} else if (tok2->str() == ";")
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (bailout)
|
|
||||||
continue;
|
continue;
|
||||||
|
if (!isConstStatement(tok))
|
||||||
// no warning if this is the last statement in a ({})
|
|
||||||
for (const Token *tok2 = tok->next(); tok2; tok2 = tok2->next()) {
|
|
||||||
if (tok2->str() == "(")
|
|
||||||
tok2 = tok2->link();
|
|
||||||
else if (Token::Match(tok2, "[;{}]")) {
|
|
||||||
bailout = Token::simpleMatch(tok2, "; } )");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bailout)
|
|
||||||
continue;
|
continue;
|
||||||
|
if (isVoidStmt(tok))
|
||||||
constStatementError(tok->next(), tok->next()->isNumber() ? "numeric" : "string");
|
continue;
|
||||||
|
bool inconclusive = Token::Match(tok, "%cop%");
|
||||||
|
if (mSettings->inconclusive || !inconclusive)
|
||||||
|
constStatementError(tok, tok->isNumber() ? "numeric" : "string", inconclusive);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckOther::constStatementError(const Token *tok, const std::string &type)
|
void CheckOther::constStatementError(const Token *tok, const std::string &type, bool inconclusive)
|
||||||
{
|
{
|
||||||
reportError(tok, Severity::warning, "constStatement", "Redundant code: Found a statement that begins with " + type + " constant.", CWE398, false);
|
std::string msg;
|
||||||
|
if (Token::simpleMatch(tok, "=="))
|
||||||
|
msg = "Found suspicious equality comparison. Did you intend to assign a value instead?";
|
||||||
|
else if (Token::Match(tok, ",|!|~|%cop%"))
|
||||||
|
msg = "Found suspicious operator '" + tok->str() + "'";
|
||||||
|
else if (Token::Match(tok, "%var%"))
|
||||||
|
msg = "Unused variable value '" + tok->str() + "'";
|
||||||
|
else
|
||||||
|
msg = "Redundant code: Found a statement that begins with " + type + " constant.";
|
||||||
|
reportError(tok, Severity::warning, "constStatement", msg, CWE398, inconclusive);
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
|
@ -83,6 +83,7 @@ public:
|
||||||
checkOther.checkFuncArgNamesDifferent();
|
checkOther.checkFuncArgNamesDifferent();
|
||||||
checkOther.checkShadowVariables();
|
checkOther.checkShadowVariables();
|
||||||
checkOther.checkConstArgument();
|
checkOther.checkConstArgument();
|
||||||
|
checkOther.checkIncompleteStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Run checks against the simplified token list */
|
/** @brief Run checks against the simplified token list */
|
||||||
|
@ -93,7 +94,6 @@ public:
|
||||||
checkOther.clarifyCalculation();
|
checkOther.clarifyCalculation();
|
||||||
checkOther.clarifyStatement();
|
checkOther.clarifyStatement();
|
||||||
checkOther.checkPassByReference();
|
checkOther.checkPassByReference();
|
||||||
checkOther.checkIncompleteStatement();
|
|
||||||
checkOther.checkCastIntToCharAndBack();
|
checkOther.checkCastIntToCharAndBack();
|
||||||
|
|
||||||
checkOther.checkMisusedScopedObject();
|
checkOther.checkMisusedScopedObject();
|
||||||
|
@ -101,7 +101,6 @@ public:
|
||||||
|
|
||||||
checkOther.checkInvalidFree();
|
checkOther.checkInvalidFree();
|
||||||
checkOther.checkRedundantCopy();
|
checkOther.checkRedundantCopy();
|
||||||
checkOther.checkSuspiciousEqualityComparison();
|
|
||||||
checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse();
|
checkOther.checkComparisonFunctionIsAlwaysTrueOrFalse();
|
||||||
checkOther.checkAccessOfMovedVariable();
|
checkOther.checkAccessOfMovedVariable();
|
||||||
}
|
}
|
||||||
|
@ -149,9 +148,6 @@ public:
|
||||||
/** @brief %Check for code like 'case A||B:'*/
|
/** @brief %Check for code like 'case A||B:'*/
|
||||||
void checkSuspiciousCaseInSwitch();
|
void checkSuspiciousCaseInSwitch();
|
||||||
|
|
||||||
/** @brief %Check for code like 'case A||B:'*/
|
|
||||||
void checkSuspiciousEqualityComparison();
|
|
||||||
|
|
||||||
/** @brief %Check for objects that are destroyed immediately */
|
/** @brief %Check for objects that are destroyed immediately */
|
||||||
void checkMisusedScopedObject();
|
void checkMisusedScopedObject();
|
||||||
|
|
||||||
|
@ -228,7 +224,7 @@ private:
|
||||||
void cstyleCastError(const Token *tok);
|
void cstyleCastError(const Token *tok);
|
||||||
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
|
void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive);
|
||||||
void passedByValueError(const Token *tok, const std::string &parname, bool inconclusive);
|
void passedByValueError(const Token *tok, const std::string &parname, bool inconclusive);
|
||||||
void constStatementError(const Token *tok, const std::string &type);
|
void constStatementError(const Token *tok, const std::string &type, bool inconclusive);
|
||||||
void signedCharArrayIndexError(const Token *tok);
|
void signedCharArrayIndexError(const Token *tok);
|
||||||
void unknownSignCharArrayIndexError(const Token *tok);
|
void unknownSignCharArrayIndexError(const Token *tok);
|
||||||
void charBitOpError(const Token *tok);
|
void charBitOpError(const Token *tok);
|
||||||
|
@ -241,7 +237,6 @@ private:
|
||||||
void redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var);
|
void redundantCopyInSwitchError(const Token *tok1, const Token* tok2, const std::string &var);
|
||||||
void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname);
|
void redundantBitwiseOperationInSwitchError(const Token *tok, const std::string &varname);
|
||||||
void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString);
|
void suspiciousCaseInSwitchError(const Token* tok, const std::string& operatorString);
|
||||||
void suspiciousEqualityComparisonError(const Token* tok);
|
|
||||||
void selfAssignmentError(const Token *tok, const std::string &varname);
|
void selfAssignmentError(const Token *tok, const std::string &varname);
|
||||||
void misusedScopeObjectError(const Token *tok, const std::string &varname);
|
void misusedScopeObjectError(const Token *tok, const std::string &varname);
|
||||||
void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors);
|
void duplicateBranchError(const Token *tok1, const Token *tok2, ErrorPath errors);
|
||||||
|
@ -298,7 +293,7 @@ private:
|
||||||
c.checkCastIntToCharAndBackError(nullptr, "func_name");
|
c.checkCastIntToCharAndBackError(nullptr, "func_name");
|
||||||
c.cstyleCastError(nullptr);
|
c.cstyleCastError(nullptr);
|
||||||
c.passedByValueError(nullptr, "parametername", false);
|
c.passedByValueError(nullptr, "parametername", false);
|
||||||
c.constStatementError(nullptr, "type");
|
c.constStatementError(nullptr, "type", false);
|
||||||
c.signedCharArrayIndexError(nullptr);
|
c.signedCharArrayIndexError(nullptr);
|
||||||
c.unknownSignCharArrayIndexError(nullptr);
|
c.unknownSignCharArrayIndexError(nullptr);
|
||||||
c.charBitOpError(nullptr);
|
c.charBitOpError(nullptr);
|
||||||
|
@ -306,7 +301,6 @@ private:
|
||||||
c.redundantAssignmentInSwitchError(nullptr, nullptr, "var");
|
c.redundantAssignmentInSwitchError(nullptr, nullptr, "var");
|
||||||
c.redundantCopyInSwitchError(nullptr, nullptr, "var");
|
c.redundantCopyInSwitchError(nullptr, nullptr, "var");
|
||||||
c.suspiciousCaseInSwitchError(nullptr, "||");
|
c.suspiciousCaseInSwitchError(nullptr, "||");
|
||||||
c.suspiciousEqualityComparisonError(nullptr);
|
|
||||||
c.selfAssignmentError(nullptr, "varname");
|
c.selfAssignmentError(nullptr, "varname");
|
||||||
c.clarifyCalculationError(nullptr, "+");
|
c.clarifyCalculationError(nullptr, "+");
|
||||||
c.clarifyStatementError(nullptr);
|
c.clarifyStatementError(nullptr);
|
||||||
|
|
|
@ -33,10 +33,11 @@ public:
|
||||||
private:
|
private:
|
||||||
Settings settings;
|
Settings settings;
|
||||||
|
|
||||||
void check(const char code[]) {
|
void check(const char code[], bool inconclusive = false) {
|
||||||
// Clear the error buffer..
|
// Clear the error buffer..
|
||||||
errout.str("");
|
errout.str("");
|
||||||
|
|
||||||
|
settings.inconclusive = inconclusive;
|
||||||
|
|
||||||
// Raw tokens..
|
// Raw tokens..
|
||||||
std::vector<std::string> files(1, "test.cpp");
|
std::vector<std::string> files(1, "test.cpp");
|
||||||
|
@ -52,7 +53,6 @@ private:
|
||||||
Tokenizer tokenizer(&settings, this);
|
Tokenizer tokenizer(&settings, this);
|
||||||
tokenizer.createTokens(&tokens2);
|
tokenizer.createTokens(&tokens2);
|
||||||
tokenizer.simplifyTokens1("");
|
tokenizer.simplifyTokens1("");
|
||||||
tokenizer.simplifyTokenList2();
|
|
||||||
|
|
||||||
// Check for incomplete statements..
|
// Check for incomplete statements..
|
||||||
CheckOther checkOther(&tokenizer, &settings, this);
|
CheckOther checkOther(&tokenizer, &settings, this);
|
||||||
|
@ -82,6 +82,8 @@ private:
|
||||||
TEST_CASE(cpp11init2); // #8449
|
TEST_CASE(cpp11init2); // #8449
|
||||||
TEST_CASE(block); // ({ do_something(); 0; })
|
TEST_CASE(block); // ({ do_something(); 0; })
|
||||||
TEST_CASE(mapindex);
|
TEST_CASE(mapindex);
|
||||||
|
TEST_CASE(commaoperator);
|
||||||
|
TEST_CASE(redundantstmts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void test1() {
|
void test1() {
|
||||||
|
@ -299,6 +301,40 @@ private:
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// #8827
|
||||||
|
void commaoperator() {
|
||||||
|
check("void foo(int,const char*,int);\n"
|
||||||
|
"void f(int value) {\n"
|
||||||
|
" foo(42,\"test\",42),(value&42);\n"
|
||||||
|
"}\n");
|
||||||
|
ASSERT_EQUALS("[test.cpp:3]: (warning) Found suspicious operator ','\n", errout.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// #8451
|
||||||
|
void redundantstmts() {
|
||||||
|
check("void f1(int x) {\n"
|
||||||
|
" 1;\n"
|
||||||
|
" (1);\n"
|
||||||
|
" (char)1;\n"
|
||||||
|
" ((char)1);\n"
|
||||||
|
" !x;\n"
|
||||||
|
" (!x);\n"
|
||||||
|
" (unsigned int)!x;\n"
|
||||||
|
" ~x;\n"
|
||||||
|
"}\n", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:2]: (warning) Redundant code: Found a statement that begins with numeric constant.\n"
|
||||||
|
"[test.cpp:3]: (warning) Redundant code: Found a statement that begins with numeric constant.\n"
|
||||||
|
"[test.cpp:4]: (warning) Redundant code: Found a statement that begins with string constant.\n"
|
||||||
|
"[test.cpp:5]: (warning) Redundant code: Found a statement that begins with string constant.\n"
|
||||||
|
"[test.cpp:6]: (warning, inconclusive) Found suspicious operator '!'\n"
|
||||||
|
"[test.cpp:7]: (warning, inconclusive) Found suspicious operator '!'\n"
|
||||||
|
"[test.cpp:9]: (warning, inconclusive) Found suspicious operator '~'\n", errout.str());
|
||||||
|
|
||||||
|
check("void f1(int x) { x; }", true);
|
||||||
|
ASSERT_EQUALS("[test.cpp:1]: (warning) Unused variable value 'x'\n", errout.str());
|
||||||
|
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestIncompleteStatement)
|
REGISTER_TEST(TestIncompleteStatement)
|
||||||
|
|
|
@ -1988,7 +1988,7 @@ private:
|
||||||
" switch (a)\n"
|
" switch (a)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" case 2:\n"
|
" case 2:\n"
|
||||||
" y;\n"
|
" (void)y;\n"
|
||||||
" case 3:\n"
|
" case 3:\n"
|
||||||
" ++y;\n"
|
" ++y;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
@ -2042,7 +2042,7 @@ private:
|
||||||
" switch (a)\n"
|
" switch (a)\n"
|
||||||
" {\n"
|
" {\n"
|
||||||
" case 2:\n"
|
" case 2:\n"
|
||||||
" y;\n"
|
" (void)y;\n"
|
||||||
" case 3:\n"
|
" case 3:\n"
|
||||||
" --y;\n"
|
" --y;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
|
@ -2741,8 +2741,12 @@ private:
|
||||||
|
|
||||||
// #4711 lambda functions
|
// #4711 lambda functions
|
||||||
check("int f() {\n"
|
check("int f() {\n"
|
||||||
" return g([](int x){x+1; return x;});\n"
|
" return g([](int x){(void)x+1; return x;});\n"
|
||||||
"}", nullptr, false, false, false);
|
"}",
|
||||||
|
nullptr,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
// #4756
|
// #4756
|
||||||
|
@ -2754,7 +2758,7 @@ private:
|
||||||
" __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff) << 8)));\n"
|
" __v = ((unsigned short int) ((((__x) >> 8) & 0xff) | (((__x) & 0xff) << 8)));\n"
|
||||||
" else\n"
|
" else\n"
|
||||||
" __asm__ (\"rorw $8, %w0\" : \"=r\" (__v) : \"0\" (__x) : \"cc\");\n"
|
" __asm__ (\"rorw $8, %w0\" : \"=r\" (__v) : \"0\" (__x) : \"cc\");\n"
|
||||||
" __v;\n"
|
" (void)__v;\n"
|
||||||
" }));\n"
|
" }));\n"
|
||||||
"}", nullptr, false, false, false);
|
"}", nullptr, false, false, false);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
@ -5468,13 +5472,13 @@ private:
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" int a; a = 123;\n"
|
" int a; a = 123;\n"
|
||||||
" a << -1;\n"
|
" (void)(a << -1);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str());
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" int a; a = 123;\n"
|
" int a; a = 123;\n"
|
||||||
" a >> -1;\n"
|
" (void)(a >> -1);\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Shifting by a negative value is undefined behaviour\n", errout.str());
|
||||||
check("void foo()\n"
|
check("void foo()\n"
|
||||||
|
|
Loading…
Reference in New Issue