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:
parent
2595b82634
commit
dc0b3527ad
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue