Merge branch 'main' of https://github.com/danmar/cppcheck into main
This commit is contained in:
commit
601ca6b3c5
|
@ -1668,7 +1668,8 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (!Token::Match(tok, "{|("))
|
if (!Token::Match(tok, "{|("))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
tok = tok->astOperand1();
|
if (tok->astOperand2())
|
||||||
|
tok = tok->astOperand1();
|
||||||
while (tok && (tok->isUnaryOp("*") || tok->str() == "["))
|
while (tok && (tok->isUnaryOp("*") || tok->str() == "["))
|
||||||
tok = tok->astOperand1();
|
tok = tok->astOperand1();
|
||||||
while (Token::simpleMatch(tok, "."))
|
while (Token::simpleMatch(tok, "."))
|
||||||
|
@ -1690,21 +1691,31 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn)
|
||||||
return tok;
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr)
|
std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr)
|
||||||
{
|
{
|
||||||
std::vector<const Variable*> result;
|
std::vector<const Variable*> result;
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return result;
|
return result;
|
||||||
if (tok->function())
|
if (tok->function()) {
|
||||||
return {tok->function()->getArgumentVar(argnr)};
|
const Variable* argvar = tok->function()->getArgumentVar(argnr);
|
||||||
if (Token::Match(tok->previous(), "%type% (|{") || tok->variable()) {
|
if (argvar)
|
||||||
const bool constructor = tok->variable() && tok->variable()->nameToken() == tok;
|
return {argvar};
|
||||||
|
else
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (Token::Match(tok->previous(), "%type% (|{") || Token::simpleMatch(tok, "{") || tok->variable()) {
|
||||||
|
const bool constructor = Token::simpleMatch(tok, "{") || (tok->variable() && tok->variable()->nameToken() == tok);
|
||||||
const Type* type = Token::typeOf(tok);
|
const Type* type = Token::typeOf(tok);
|
||||||
if (!type)
|
if (!type)
|
||||||
return result;
|
return result;
|
||||||
const Scope* typeScope = type->classScope;
|
const Scope* typeScope = type->classScope;
|
||||||
if (!typeScope)
|
if (!typeScope)
|
||||||
return result;
|
return result;
|
||||||
|
// Aggregate constructor
|
||||||
|
if (Token::simpleMatch(tok, "{") && typeScope->numConstructors == 0 && argnr < typeScope->varlist.size()) {
|
||||||
|
auto it = std::next(typeScope->varlist.begin(), argnr);
|
||||||
|
return {&*it};
|
||||||
|
}
|
||||||
const int argCount = numberOfArguments(tok);
|
const int argCount = numberOfArguments(tok);
|
||||||
for (const Function &function : typeScope->functionList) {
|
for (const Function &function : typeScope->functionList) {
|
||||||
if (function.argCount() < argCount)
|
if (function.argCount() < argCount)
|
||||||
|
@ -1713,7 +1724,9 @@ static std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr)
|
||||||
continue;
|
continue;
|
||||||
if (!constructor && !Token::simpleMatch(function.token, "operator()"))
|
if (!constructor && !Token::simpleMatch(function.token, "operator()"))
|
||||||
continue;
|
continue;
|
||||||
result.push_back(function.getArgumentVar(argnr));
|
const Variable* argvar = function.getArgumentVar(argnr);
|
||||||
|
if (argvar)
|
||||||
|
result.push_back(argvar);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -1726,6 +1739,17 @@ static bool isCPPCastKeyword(const Token* tok)
|
||||||
return endsWith(tok->str(), "_cast", 5);
|
return endsWith(tok->str(), "_cast", 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool isTrivialConstructor(const Token* tok)
|
||||||
|
{
|
||||||
|
const Token* typeTok = nullptr;
|
||||||
|
const Type* t = Token::typeOf(tok, &typeTok);
|
||||||
|
if (t)
|
||||||
|
return false;
|
||||||
|
if (typeTok->valueType() && typeTok->valueType()->isPrimitive())
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive)
|
bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
|
@ -1743,6 +1767,8 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti
|
||||||
tok = getTokenArgumentFunction(tok, argnr);
|
tok = getTokenArgumentFunction(tok, argnr);
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return false; // not a function => variable not changed
|
return false; // not a function => variable not changed
|
||||||
|
if (Token::simpleMatch(tok, "{") && isTrivialConstructor(tok))
|
||||||
|
return false;
|
||||||
if (tok->isKeyword() && !isCPPCastKeyword(tok))
|
if (tok->isKeyword() && !isCPPCastKeyword(tok))
|
||||||
return false;
|
return false;
|
||||||
const Token * parenTok = tok->next();
|
const Token * parenTok = tok->next();
|
||||||
|
@ -2303,19 +2329,19 @@ std::vector<const Variable*> getLHSVariables(const Token* tok)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const Variable *getLHSVariableRecursive(const Token *tok)
|
static const Token* getLHSVariableRecursive(const Token* tok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (Token::Match(tok, "*|&|&&|[")) {
|
if (Token::Match(tok, "*|&|&&|[")) {
|
||||||
const Variable *var = getLHSVariableRecursive(tok->astOperand1());
|
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
|
||||||
if (var || Token::simpleMatch(tok, "["))
|
if ((vartok && vartok->variable()) || Token::simpleMatch(tok, "["))
|
||||||
return var;
|
return vartok;
|
||||||
return getLHSVariableRecursive(tok->astOperand2());
|
return getLHSVariableRecursive(tok->astOperand2());
|
||||||
}
|
}
|
||||||
if (Token::Match(tok->previous(), "this . %var%"))
|
if (Token::Match(tok->previous(), "this . %var%"))
|
||||||
return tok->next()->variable();
|
return tok->next();
|
||||||
return tok->variable();
|
return tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Variable *getLHSVariable(const Token *tok)
|
const Variable *getLHSVariable(const Token *tok)
|
||||||
|
@ -2326,7 +2352,24 @@ const Variable *getLHSVariable(const Token *tok)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable())
|
if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable())
|
||||||
return tok->astOperand1()->variable();
|
return tok->astOperand1()->variable();
|
||||||
return getLHSVariableRecursive(tok->astOperand1());
|
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
|
||||||
|
if (!vartok)
|
||||||
|
return nullptr;
|
||||||
|
return vartok->variable();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Token* getLHSVariableToken(const Token* tok)
|
||||||
|
{
|
||||||
|
if (!Token::Match(tok, "%assign%"))
|
||||||
|
return nullptr;
|
||||||
|
if (!tok->astOperand1())
|
||||||
|
return nullptr;
|
||||||
|
if (tok->astOperand1()->varId() > 0)
|
||||||
|
return tok->astOperand1();
|
||||||
|
const Token* vartok = getLHSVariableRecursive(tok->astOperand1());
|
||||||
|
if (!vartok)
|
||||||
|
return tok->astOperand1();
|
||||||
|
return vartok;
|
||||||
}
|
}
|
||||||
|
|
||||||
const Token* findAllocFuncCallToken(const Token *expr, const Library &library)
|
const Token* findAllocFuncCallToken(const Token *expr, const Library &library)
|
||||||
|
|
|
@ -188,6 +188,8 @@ bool isReturnScope(const Token* const endToken,
|
||||||
/// Return the token to the function and the argument number
|
/// Return the token to the function and the argument number
|
||||||
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
|
const Token * getTokenArgumentFunction(const Token * tok, int& argn);
|
||||||
|
|
||||||
|
std::vector<const Variable*> getArgumentVars(const Token* tok, int argnr);
|
||||||
|
|
||||||
/** Is variable changed by function call?
|
/** Is variable changed by function call?
|
||||||
* In case the answer of the question is inconclusive, e.g. because the function declaration is not known
|
* In case the answer of the question is inconclusive, e.g. because the function declaration is not known
|
||||||
* the return value is false and the output parameter inconclusive is set to true
|
* the return value is false and the output parameter inconclusive is set to true
|
||||||
|
@ -279,6 +281,8 @@ bool isConstVarExpression(const Token *tok, const char * skipMatch = nullptr);
|
||||||
|
|
||||||
const Variable *getLHSVariable(const Token *tok);
|
const Variable *getLHSVariable(const Token *tok);
|
||||||
|
|
||||||
|
const Token* getLHSVariableToken(const Token* tok);
|
||||||
|
|
||||||
std::vector<const Variable*> getLHSVariables(const Token* tok);
|
std::vector<const Variable*> getLHSVariables(const Token* tok);
|
||||||
|
|
||||||
/** Find a allocation function call in expression, so result of expression is allocated memory/resource. */
|
/** Find a allocation function call in expression, so result of expression is allocated memory/resource. */
|
||||||
|
|
|
@ -1273,6 +1273,8 @@ public:
|
||||||
static MatchResult matchParameter(const ValueType *call, const ValueType *func);
|
static MatchResult matchParameter(const ValueType *call, const ValueType *func);
|
||||||
static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar);
|
static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar);
|
||||||
|
|
||||||
|
bool isPrimitive() const { return (type >= ValueType::Type::BOOL); }
|
||||||
|
|
||||||
bool isIntegral() const {
|
bool isIntegral() const {
|
||||||
return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT);
|
return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2257,10 +2257,12 @@ void Token::type(const ::Type *t)
|
||||||
tokType(eName);
|
tokType(eName);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ::Type *Token::typeOf(const Token *tok)
|
const ::Type* Token::typeOf(const Token* tok, const Token** typeTok)
|
||||||
{
|
{
|
||||||
if (!tok)
|
if (!tok)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
if (typeTok != nullptr)
|
||||||
|
*typeTok = tok;
|
||||||
if (Token::simpleMatch(tok, "return")) {
|
if (Token::simpleMatch(tok, "return")) {
|
||||||
const Scope *scope = tok->scope();
|
const Scope *scope = tok->scope();
|
||||||
if (!scope)
|
if (!scope)
|
||||||
|
@ -2282,14 +2284,30 @@ const ::Type *Token::typeOf(const Token *tok)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return function->retType;
|
return function->retType;
|
||||||
} else if (Token::Match(tok->previous(), "%type%|= (|{")) {
|
} else if (Token::Match(tok->previous(), "%type%|= (|{")) {
|
||||||
return typeOf(tok->previous());
|
return typeOf(tok->previous(), typeTok);
|
||||||
} else if (Token::simpleMatch(tok, "=")) {
|
} else if (Token::simpleMatch(tok, "=")) {
|
||||||
return Token::typeOf(tok->astOperand1());
|
return Token::typeOf(getLHSVariableToken(tok), typeTok);
|
||||||
} else if (Token::simpleMatch(tok, ".")) {
|
} else if (Token::simpleMatch(tok, ".")) {
|
||||||
return Token::typeOf(tok->astOperand2());
|
return Token::typeOf(tok->astOperand2(), typeTok);
|
||||||
} else if (Token::simpleMatch(tok, "[")) {
|
} else if (Token::simpleMatch(tok, "[")) {
|
||||||
return Token::typeOf(tok->astOperand1());
|
return Token::typeOf(tok->astOperand1(), typeTok);
|
||||||
|
} else if (Token::simpleMatch(tok, "{")) {
|
||||||
|
int argnr;
|
||||||
|
const Token* ftok = getTokenArgumentFunction(tok, argnr);
|
||||||
|
if (argnr < 0)
|
||||||
|
return nullptr;
|
||||||
|
if (!ftok)
|
||||||
|
return nullptr;
|
||||||
|
if (ftok == tok)
|
||||||
|
return nullptr;
|
||||||
|
std::vector<const Variable*> vars = getArgumentVars(ftok, argnr);
|
||||||
|
if (vars.empty())
|
||||||
|
return nullptr;
|
||||||
|
if (std::all_of(
|
||||||
|
vars.begin(), vars.end(), [&](const Variable* var) { return var->type() == vars.front()->type(); }))
|
||||||
|
return vars.front()->type();
|
||||||
}
|
}
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1010,7 +1010,7 @@ public:
|
||||||
return mTokType == eType ? mImpl->mType : nullptr;
|
return mTokType == eType ? mImpl->mType : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const ::Type *typeOf(const Token *tok);
|
static const ::Type* typeOf(const Token* tok, const Token** typeTok = nullptr);
|
||||||
|
|
||||||
static std::pair<const Token*, const Token*> typeDecl(const Token * tok);
|
static std::pair<const Token*, const Token*> typeDecl(const Token * tok);
|
||||||
|
|
||||||
|
|
|
@ -723,6 +723,8 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti
|
||||||
for (const ValueFlow::Value &val : tok->values()) {
|
for (const ValueFlow::Value &val : tok->values()) {
|
||||||
if (!val.isIntValue())
|
if (!val.isIntValue())
|
||||||
continue;
|
continue;
|
||||||
|
if (val.isImpossible() && val.intvalue != 0)
|
||||||
|
continue;
|
||||||
ValueFlow::Value v(val);
|
ValueFlow::Value v(val);
|
||||||
v.intvalue = !v.intvalue;
|
v.intvalue = !v.intvalue;
|
||||||
setTokenValue(parent, v, settings);
|
setTokenValue(parent, v, settings);
|
||||||
|
|
|
@ -381,6 +381,86 @@ private:
|
||||||
"}\n", true);
|
"}\n", true);
|
||||||
ASSERT_EQUALS("", errout.str());
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct T {\n"
|
||||||
|
" std::vector<int>* v;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" std::vector<T> t;\n"
|
||||||
|
"};\n"
|
||||||
|
"long g(S& s);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" std::vector<int> ArrS;\n"
|
||||||
|
" S s = { { { &ArrS } } };\n"
|
||||||
|
" g(s);\n"
|
||||||
|
" return ArrS[0];\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct T {\n"
|
||||||
|
" std::vector<int>* v;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" std::vector<std::vector<T>> t;\n"
|
||||||
|
"};\n"
|
||||||
|
"long g(S& s);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" std::vector<int> ArrS;\n"
|
||||||
|
" S s = { { { { &ArrS } } } };\n"
|
||||||
|
" g(s);\n"
|
||||||
|
" return ArrS[0];\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct T {\n"
|
||||||
|
" std::vector<int>* v;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" T t;\n"
|
||||||
|
"};\n"
|
||||||
|
"long g(S& s);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" std::vector<int> ArrS;\n"
|
||||||
|
" S s { { &ArrS } };\n"
|
||||||
|
" g(s);\n"
|
||||||
|
" return ArrS[0];\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct T {\n"
|
||||||
|
" std::vector<int>* v;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" std::vector<T> t;\n"
|
||||||
|
"};\n"
|
||||||
|
"long g(S& s);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" std::vector<int> ArrS;\n"
|
||||||
|
" S s { { { &ArrS } } };\n"
|
||||||
|
" g(s);\n"
|
||||||
|
" return ArrS[0];\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
|
check("struct T {\n"
|
||||||
|
" std::vector<int>* v;\n"
|
||||||
|
"};\n"
|
||||||
|
"struct S {\n"
|
||||||
|
" std::vector<std::vector<T>> t;\n"
|
||||||
|
"};\n"
|
||||||
|
"long g(S& s);\n"
|
||||||
|
"int f() {\n"
|
||||||
|
" std::vector<int> ArrS;\n"
|
||||||
|
" S s { { { { &ArrS } } } };\n"
|
||||||
|
" g(s);\n"
|
||||||
|
" return ArrS[0];\n"
|
||||||
|
"}\n",
|
||||||
|
true);
|
||||||
|
ASSERT_EQUALS("", errout.str());
|
||||||
|
|
||||||
checkNormal("extern void Bar(const double, const double);\n"
|
checkNormal("extern void Bar(const double, const double);\n"
|
||||||
"void f(std::vector<double> &r, const double ) {\n"
|
"void f(std::vector<double> &r, const double ) {\n"
|
||||||
" std::vector<double> result;\n"
|
" std::vector<double> result;\n"
|
||||||
|
|
|
@ -141,6 +141,7 @@ private:
|
||||||
TEST_CASE(valueFlowUnknownMixedOperators);
|
TEST_CASE(valueFlowUnknownMixedOperators);
|
||||||
TEST_CASE(valueFlowIdempotent);
|
TEST_CASE(valueFlowIdempotent);
|
||||||
TEST_CASE(valueFlowUnsigned);
|
TEST_CASE(valueFlowUnsigned);
|
||||||
|
TEST_CASE(valueFlowMod);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNotTokValue(const ValueFlow::Value &val) {
|
static bool isNotTokValue(const ValueFlow::Value &val) {
|
||||||
|
@ -2643,6 +2644,20 @@ private:
|
||||||
ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 1));
|
ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 1));
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 0));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 0));
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 1));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 1));
|
||||||
|
|
||||||
|
code = "auto f(int i) {\n"
|
||||||
|
" if (i == 0) return;\n"
|
||||||
|
" auto x = !i;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, 1));
|
||||||
|
|
||||||
|
code = "auto f(int i) {\n"
|
||||||
|
" if (i == 1) return;\n"
|
||||||
|
" auto x = !i;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowAfterConditionExpr() {
|
void valueFlowAfterConditionExpr() {
|
||||||
|
@ -5638,6 +5653,23 @@ private:
|
||||||
ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0));
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0));
|
||||||
ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, -1));
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void valueFlowMod() {
|
||||||
|
const char *code;
|
||||||
|
|
||||||
|
code = "auto f(int i) {\n"
|
||||||
|
" auto x = i % 2;\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, 2));
|
||||||
|
|
||||||
|
code = "auto f(int i) {\n"
|
||||||
|
" auto x = !(i % 2);\n"
|
||||||
|
" return x;\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 0));
|
||||||
|
ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 1));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestValueFlow)
|
REGISTER_TEST(TestValueFlow)
|
||||||
|
|
Loading…
Reference in New Issue