Fix issue 9311: False positive duplicateCondition "same if condition" with pointer inside array of struct (#2166)

* Check for typeOf through an array

* Handle array constructors

* Format

* Fix compile error on gcc 4.8
This commit is contained in:
Paul Fultz II 2019-09-10 12:41:35 -05:00 committed by Daniel Marjamäki
parent 2595b82634
commit dc0b3527ad
5 changed files with 71 additions and 45 deletions

View File

@ -942,7 +942,7 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, nonneg int
isVariableChangedByFunctionCall(tok->astOperand2(), indirect, varid, settings, inconclusive);
}
static bool isScopeBracket(const Token *tok)
bool isScopeBracket(const Token* tok)
{
if (!Token::Match(tok, "{|}"))
return false;
@ -1222,30 +1222,17 @@ int numberOfArguments(const Token *start)
return arguments;
}
static void getArgumentsRecursive(const Token *tok, std::vector<const Token *> *arguments, nonneg int depth)
{
++depth;
if (!tok || depth >= 100)
return;
if (tok->str() == ",") {
getArgumentsRecursive(tok->astOperand1(), arguments, depth);
getArgumentsRecursive(tok->astOperand2(), arguments, depth);
} else {
arguments->push_back(tok);
}
}
std::vector<const Token *> getArguments(const Token *ftok)
{
std::vector<const Token *> arguments;
const Token *tok = ftok->next();
if (!Token::Match(tok, "(|{"))
tok = ftok;
const Token* tok = ftok;
if (Token::Match(tok, "%name% (|{"))
tok = ftok->next();
if (!Token::Match(tok, "(|{|["))
return std::vector<const Token *>{};
const Token *startTok = tok->astOperand2();
if (!startTok && Token::simpleMatch(tok->astOperand1(), ","))
if (!startTok && tok->next() != tok->link())
startTok = tok->astOperand1();
getArgumentsRecursive(startTok, &arguments, 0);
return arguments;
return astFlatten(startTok, ",");
}
const Token *findLambdaStartToken(const Token *last)

View File

@ -191,6 +191,8 @@ bool isConstVarExpression(const Token *tok);
const Variable *getLHSVariable(const Token *tok);
bool isScopeBracket(const Token* tok);
struct PathAnalysis {
enum class Progress {
Continue,

View File

@ -1936,6 +1936,8 @@ const ::Type *Token::typeOf(const Token *tok)
return Token::typeOf(tok->astOperand1());
} else if (Token::simpleMatch(tok, ".")) {
return Token::typeOf(tok->astOperand2());
} else if (Token::simpleMatch(tok, "[")) {
return Token::typeOf(tok->astOperand1());
}
return nullptr;
}

View File

@ -3324,11 +3324,13 @@ static void valueFlowForwardLifetime(Token * tok, TokenList *tokenlist, ErrorLog
settings);
}
// Constructor
} else if (Token::Match(parent->previous(), "=|return|%type%|%var% {")) {
} else if (Token::simpleMatch(parent, "{") && !isScopeBracket(parent)) {
valueFlowLifetimeConstructor(parent, tokenlist, errorLogger, settings);
valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings);
// Function call
} else if (Token::Match(parent->previous(), "%name% (")) {
valueFlowLifetimeFunction(parent->previous(), tokenlist, errorLogger, settings);
valueFlowForwardLifetime(parent, tokenlist, errorLogger, settings);
// Variable
} else if (tok->variable()) {
const Variable *var = tok->variable();
@ -3567,37 +3569,55 @@ static void valueFlowLifetimeFunction(Token *tok, TokenList *tokenlist, ErrorLog
}
}
static void valueFlowLifetimeConstructor(Token *tok, TokenList *tokenlist, ErrorLogger *errorLogger, const Settings *settings)
static void valueFlowLifetimeConstructor(Token* tok,
const Type* t,
TokenList* tokenlist,
ErrorLogger* errorLogger,
const Settings* settings)
{
if (!t)
return;
if (!Token::Match(tok, "(|{"))
return;
const Scope* scope = t->classScope;
if (!scope)
return;
// Only support aggregate constructors for now
if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) {
std::vector<const Token*> args = getArguments(tok);
std::size_t i = 0;
for (const Variable& var : scope->varlist) {
if (i >= args.size())
break;
const Token* argtok = args[i];
LifetimeStore ls{
argtok, "Passed to constructor of '" + t->name() + "'.", ValueFlow::Value::LifetimeKind::Object};
if (var.isReference() || var.isRValueReference()) {
ls.byRef(tok, tokenlist, errorLogger, settings);
} else {
ls.byVal(tok, tokenlist, errorLogger, settings);
}
i++;
}
}
}
static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, ErrorLogger* errorLogger, const Settings* settings)
{
if (!Token::Match(tok, "(|{"))
return;
if (const Type *t = Token::typeOf(tok->previous())) {
const Scope *scope = t->classScope;
if (!scope)
return;
// Only support aggregate constructors for now
if (scope->numConstructors == 0 && t->derivedFrom.empty() && (t->isClassType() || t->isStructType())) {
std::vector<const Token *> args = getArguments(tok);
std::size_t i = 0;
for (const Variable &var : scope->varlist) {
if (i >= args.size())
break;
const Token *argtok = args[i];
LifetimeStore ls{argtok, "Passed to constructor of '" + t->name() + "'.", ValueFlow::Value::LifetimeKind::Object};
if (var.isReference() || var.isRValueReference()) {
ls.byRef(tok, tokenlist, errorLogger, settings);
} else {
ls.byVal(tok, tokenlist, errorLogger, settings);
}
i++;
}
}
} else if (Token::simpleMatch(tok, "{") && (astIsContainer(tok->astParent()) || astIsPointer(tok->astParent()))) {
Token* parent = tok->astParent();
while (Token::simpleMatch(parent, ","))
parent = parent->astParent();
if (Token::simpleMatch(parent, "{") && (astIsContainer(parent->astParent()) || astIsPointer(parent->astParent()))) {
valueFlowLifetimeConstructor(tok, Token::typeOf(parent->previous()), tokenlist, errorLogger, settings);
} else if (Token::simpleMatch(tok, "{") && (astIsContainer(parent) || astIsPointer(parent))) {
std::vector<const Token *> args = getArguments(tok);
for (const Token *argtok : args) {
LifetimeStore ls{argtok, "Passed to initializer list.", ValueFlow::Value::LifetimeKind::Object};
ls.byVal(tok, tokenlist, errorLogger, settings);
}
} else if (const Type* t = Token::typeOf(tok->previous())) {
valueFlowLifetimeConstructor(tok, t, tokenlist, errorLogger, settings);
}
}

View File

@ -3351,6 +3351,21 @@ private:
"}\n");
ASSERT_EQUALS("", errout.str());
// #9311
check("struct c {\n"
" int* p;\n"
"};\n"
"void g(struct c* v);\n"
"void f() {\n"
" int a = 0;\n"
" int b = 0;\n"
" struct c d[] = {{&a}, {&b}};\n"
" g(d);\n"
" if (a) {}\n"
" if (b) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
// #8993
check("void f(const std::string& x) {\n"
" auto y = x;\n"