Merge branch 'main' into lifetime-subfunction
This commit is contained in:
commit
34d65e25d6
|
@ -137,7 +137,8 @@ class Token:
|
|||
isUnsigned Is this token a unsigned type
|
||||
isSigned Is this token a signed type
|
||||
isExpandedMacro Is this token a expanded macro token
|
||||
isSplittedVarDecl Is this token a splitted variable declaration. "int a,b; => int a; int b;"
|
||||
isSplittedVarDeclComma Is this a comma changed to semicolon in a splitted variable declaration ('int a,b;' => 'int a; int b;')
|
||||
isSplittedVarDeclEq Is this a '=' changed to semicolon in a splitted variable declaration ('int a=5;' => 'int a; a=5;')
|
||||
varId varId for token, each variable has a unique non-zero id
|
||||
variable Variable information for this token. See the Variable class.
|
||||
function If this token points at a function call, this attribute has the Function
|
||||
|
@ -186,7 +187,8 @@ class Token:
|
|||
isUnsigned = False
|
||||
isSigned = False
|
||||
isExpandedMacro = False
|
||||
isSplittedVarDecl = False
|
||||
isSplittedVarDeclComma = False
|
||||
isSplittedVarDeclEq = False
|
||||
varId = None
|
||||
variableId = None
|
||||
variable = None
|
||||
|
@ -247,8 +249,10 @@ class Token:
|
|||
self.isLogicalOp = True
|
||||
if element.get('isExpandedMacro'):
|
||||
self.isExpandedMacro = True
|
||||
if element.get('isSplittedVarDecl'):
|
||||
self.isSplittedVarDecl = True
|
||||
if element.get('isSplittedVarDeclComma'):
|
||||
self.isSplittedVarDeclComma = True
|
||||
if element.get('isSplittedVarDeclEq'):
|
||||
self.isSplittedVarDeclEq = True
|
||||
self.linkId = element.get('link')
|
||||
self.link = None
|
||||
if element.get('varId'):
|
||||
|
@ -279,10 +283,10 @@ class Token:
|
|||
attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned",
|
||||
"isNumber", "isInt", "isFloat", "isString", "strlen",
|
||||
"isChar", "isOp", "isArithmeticalOp", "isComparisonOp",
|
||||
"isLogicalOp", "isExpandedMacro", "isSplittedVarDecl",
|
||||
"linkId", "varId", "variableId", "functionId", "valuesId",
|
||||
"valueType", "typeScopeId", "astParentId", "astOperand1Id",
|
||||
"file", "linenr", "column"]
|
||||
"isLogicalOp", "isExpandedMacro", "isSplittedVarDeclComma",
|
||||
"isSplittedVarDeclEq","linkId", "varId", "variableId",
|
||||
"functionId", "valuesId", "valueType", "typeScopeId",
|
||||
"astParentId", "astOperand1Id", "file", "linenr", "column"]
|
||||
return "{}({})".format(
|
||||
"Token",
|
||||
", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs))
|
||||
|
|
|
@ -1640,7 +1640,7 @@ class MisraChecker:
|
|||
|
||||
def misra_12_3(self, data):
|
||||
for token in data.tokenlist:
|
||||
if token.str == ';' and (token.isSplittedVarDecl is True):
|
||||
if token.str == ';' and (token.isSplittedVarDeclComma is True):
|
||||
self.reportError(token, 12, 3)
|
||||
if token.str == ',' and token.astParent and token.astParent.str == ';':
|
||||
self.reportError(token, 12, 3)
|
||||
|
|
|
@ -14381,4 +14381,17 @@ wxItemKind kind = wxITEM_NORMAL) -->
|
|||
<arg nr="1" direction="in"/>
|
||||
<arg nr="2" default="wxBRUSHSTYLE_SOLID" direction="in"/>
|
||||
</function>
|
||||
<!-- virtual bool wxClipboard::IsSupported(const wxDataFormat & format)-->
|
||||
<function name="wxClipboard::IsSupported,wxTheClipboard::IsSupported">
|
||||
<noreturn>false</noreturn>
|
||||
<use-retval/>
|
||||
<returnValue type="bool"/>
|
||||
<arg nr="1" direction="in"/>
|
||||
</function>
|
||||
<!-- virtual bool wxClipboard::GetData(wxDataObject & data)-->
|
||||
<function name="wxClipboard::GetData,wxTheClipboard::GetData">
|
||||
<noreturn>false</noreturn>
|
||||
<returnValue type="bool"/>
|
||||
<arg nr="1" direction="out"/>
|
||||
</function>
|
||||
</def>
|
||||
|
|
|
@ -248,6 +248,9 @@ bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknow
|
|||
if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1()))
|
||||
return false;
|
||||
if (Token::Match(tok->previous(), ">|%name% (")) {
|
||||
if (tok->valueType()) {
|
||||
return tok->valueType()->reference == Reference::None;
|
||||
}
|
||||
const Token* ftok = nullptr;
|
||||
if (tok->previous()->link())
|
||||
ftok = tok->previous()->link()->previous();
|
||||
|
@ -956,7 +959,7 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token
|
|||
if (!cond1 || !cond2)
|
||||
return false;
|
||||
|
||||
if (cond1->str() == "&&" && cond2->str() == "&&") {
|
||||
if (!isNot && cond1->str() == "&&" && cond2->str() == "&&") {
|
||||
for (const Token* tok1: {
|
||||
cond1->astOperand1(), cond1->astOperand2()
|
||||
}) {
|
||||
|
|
|
@ -306,7 +306,7 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine:
|
|||
if (!var->isLocal() || var->isStatic())
|
||||
return;
|
||||
}
|
||||
if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || Token::Match(var->nameToken(), "%varid% ; %varid% =", var->declarationId())))
|
||||
if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || var->isInit()))
|
||||
return;
|
||||
if (var && var->nameToken() == tok)
|
||||
return;
|
||||
|
|
|
@ -528,6 +528,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
errorDanglingReference(tok, var, errorPath);
|
||||
continue;
|
||||
}
|
||||
// Reference to temporary
|
||||
} else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) {
|
||||
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) {
|
||||
const Token * tokvalue = lt.token;
|
||||
if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
|
||||
errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
for (const ValueFlow::Value& val:tok->values()) {
|
||||
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
|
||||
|
@ -644,6 +654,13 @@ void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow:
|
|||
reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive);
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
|
||||
{
|
||||
errorPath.emplace_back(tok, "");
|
||||
reportError(
|
||||
errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive);
|
||||
}
|
||||
|
||||
void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
|
||||
{
|
||||
errorPath.emplace_back(tok, "");
|
||||
|
|
|
@ -79,6 +79,7 @@ private:
|
|||
void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val);
|
||||
void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
|
||||
void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath);
|
||||
void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
|
||||
void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
|
||||
void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val);
|
||||
void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname);
|
||||
|
@ -94,6 +95,7 @@ private:
|
|||
c.errorReturnReference(nullptr, errorPath, false);
|
||||
c.errorDanglingReference(nullptr, nullptr, errorPath);
|
||||
c.errorReturnTempReference(nullptr, errorPath, false);
|
||||
c.errorDanglingTempReference(nullptr, errorPath, false);
|
||||
c.errorInvalidDeallocation(nullptr, nullptr);
|
||||
c.errorReturnAddressOfFunctionParameter(nullptr, "parameter");
|
||||
c.errorUselessAssignmentArg(nullptr);
|
||||
|
|
|
@ -111,11 +111,7 @@ void CheckClass::constructors()
|
|||
if (scope->numConstructors == 0 && printStyle && !usedInUnion) {
|
||||
// If there is a private variable, there should be a constructor..
|
||||
for (const Variable &var : scope->varlist) {
|
||||
const Token *initTok = var.nameToken();
|
||||
while (Token::simpleMatch(initTok->next(), "["))
|
||||
initTok = initTok->linkAt(1);
|
||||
if (var.isPrivate() && !var.isStatic() && !Token::Match(var.nameToken(), "%varid% ; %varid% =", var.declarationId()) &&
|
||||
!Token::Match(initTok, "%var%|] {|=") &&
|
||||
if (var.isPrivate() && !var.isStatic() && !var.isInit() &&
|
||||
(!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) {
|
||||
noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct");
|
||||
break;
|
||||
|
@ -947,9 +943,15 @@ void CheckClass::initializationListUsage()
|
|||
|
||||
for (const Scope *scope : mSymbolDatabase->functionScopes) {
|
||||
// Check every constructor
|
||||
if (!scope->function || (!scope->function->isConstructor()))
|
||||
if (!scope->function || !scope->function->isConstructor())
|
||||
continue;
|
||||
|
||||
// Do not warn when a delegate constructor is called
|
||||
if (const Token *initList = scope->function->constructorMemberInitialization()) {
|
||||
if (Token::Match(initList, ": %name% {|(") && initList->strAt(1) == scope->className)
|
||||
continue;
|
||||
}
|
||||
|
||||
const Scope* owner = scope->functionOf;
|
||||
for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) {
|
||||
if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on.
|
||||
|
|
|
@ -432,7 +432,7 @@ void CheckOther::checkRedundantAssignment()
|
|||
// Do not warn about redundant initialization when rhs is trivial
|
||||
// TODO : do not simplify the variable declarations
|
||||
bool isInitialization = false;
|
||||
if (Token::Match(tok->tokAt(-3), "%var% ; %var% =") && tok->previous()->variable() && tok->previous()->variable()->nameToken() == tok->tokAt(-3) && tok->tokAt(-3)->linenr() == tok->previous()->linenr()) {
|
||||
if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) {
|
||||
isInitialization = true;
|
||||
bool trivial = true;
|
||||
visitAstNodes(tok->astOperand2(),
|
||||
|
|
|
@ -59,6 +59,11 @@ static const struct CWE CWE825(825U); // Expired Pointer Dereference
|
|||
static const struct CWE CWE833(833U); // Deadlock
|
||||
static const struct CWE CWE834(834U); // Excessive Iteration
|
||||
|
||||
static bool isElementAccessYield(const Library::Container::Yield& yield)
|
||||
{
|
||||
return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX;
|
||||
}
|
||||
|
||||
void CheckStl::outOfBounds()
|
||||
{
|
||||
for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) {
|
||||
|
@ -76,9 +81,28 @@ void CheckStl::outOfBounds()
|
|||
continue;
|
||||
if (!value.errorSeverity() && !mSettings->isEnabled(Settings::WARNING))
|
||||
continue;
|
||||
if (value.intvalue == 0 && Token::Match(parent, ". %name% (") && container->getYield(parent->strAt(1)) == Library::Container::Yield::ITEM) {
|
||||
outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr);
|
||||
continue;
|
||||
if (Token::Match(parent, ". %name% (") && isElementAccessYield(container->getYield(parent->strAt(1)))) {
|
||||
if (value.intvalue == 0) {
|
||||
outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr);
|
||||
continue;
|
||||
}
|
||||
const Token* indexTok = parent->tokAt(2)->astOperand2();
|
||||
if (!indexTok)
|
||||
continue;
|
||||
const ValueFlow::Value* indexValue = indexTok->getMaxValue(false);
|
||||
if (indexValue && indexValue->intvalue >= value.intvalue) {
|
||||
outOfBoundsError(
|
||||
parent, tok->expressionString(), &value, indexTok->expressionString(), indexValue);
|
||||
continue;
|
||||
}
|
||||
if (mSettings->isEnabled(Settings::WARNING)) {
|
||||
indexValue = indexTok->getMaxValue(true);
|
||||
if (indexValue && indexValue->intvalue >= value.intvalue) {
|
||||
outOfBoundsError(
|
||||
parent, tok->expressionString(), &value, indexTok->expressionString(), indexValue);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Token::Match(tok, "%name% . %name% (") && container->getYield(tok->strAt(2)) == Library::Container::Yield::START_ITERATOR) {
|
||||
const Token *fparent = tok->tokAt(3)->astParent();
|
||||
|
|
|
@ -2512,7 +2512,7 @@ static ExprEngine::ValuePtr createStructVal(const Scope *structScope, bool unini
|
|||
std::shared_ptr<ExprEngine::StructValue> structValue = std::make_shared<ExprEngine::StructValue>(data.getNewSymbolName());
|
||||
auto uninitValue = std::make_shared<ExprEngine::UninitValue>();
|
||||
for (const Variable &member : structScope->varlist) {
|
||||
if (uninitData) {
|
||||
if (uninitData && !member.isInit()) {
|
||||
if (member.isPointer()) {
|
||||
structValue->member[member.name()] = uninitValue;
|
||||
continue;
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
#include "astutils.h"
|
||||
#include "errorlogger.h"
|
||||
#include "library.h"
|
||||
#include "mathlib.h"
|
||||
#include "platform.h"
|
||||
#include "settings.h"
|
||||
|
@ -1869,6 +1870,13 @@ Variable::Variable(const Token *name_, const std::string &clangType, const Token
|
|||
++pos;
|
||||
} while (pos < clangType.size() && clangType[pos] == '[');
|
||||
}
|
||||
|
||||
// Is there initialization in variable declaration
|
||||
const Token *initTok = mNameToken ? mNameToken->next() : nullptr;
|
||||
while (initTok && initTok->str() == "[")
|
||||
initTok = initTok->link()->next();
|
||||
if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq()))
|
||||
setFlag(fIsInit, true);
|
||||
}
|
||||
|
||||
Variable::~Variable()
|
||||
|
@ -1899,6 +1907,13 @@ const Token * Variable::declEndToken() const
|
|||
|
||||
void Variable::evaluate(const Settings* settings)
|
||||
{
|
||||
// Is there initialization in variable declaration
|
||||
const Token *initTok = mNameToken ? mNameToken->next() : nullptr;
|
||||
while (initTok && initTok->str() == "[")
|
||||
initTok = initTok->link()->next();
|
||||
if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq()))
|
||||
setFlag(fIsInit, true);
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
|
@ -5968,7 +5983,8 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
|
|||
valuetype.sign = ValueType::Sign::SIGNED;
|
||||
}
|
||||
setValueType(tok, valuetype);
|
||||
} else if (tok->link() && tok->str() == "(") {
|
||||
} else if (tok->link() && Token::Match(tok, "(|{")) {
|
||||
const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr;
|
||||
// cast
|
||||
if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) {
|
||||
ValueType valuetype;
|
||||
|
@ -5983,6 +5999,16 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
|
|||
setValueType(tok, valuetype);
|
||||
}
|
||||
|
||||
// Construct smart pointer
|
||||
else if (mSettings->library.isSmartPointer(start)) {
|
||||
ValueType valuetype;
|
||||
if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings)) {
|
||||
setValueType(tok, valuetype);
|
||||
setValueType(tok->astOperand1(), valuetype);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// function
|
||||
else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) {
|
||||
ValueType valuetype;
|
||||
|
|
|
@ -199,6 +199,7 @@ class CPPCHECKLIB Variable {
|
|||
fIsVolatile = (1 << 13), /** @brief volatile */
|
||||
fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */
|
||||
fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */
|
||||
fIsInit = (1 << 16), /** @brief Is variable initialized in declaration */
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -501,6 +502,14 @@ public:
|
|||
return getFlag(fHasDefault);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is variable initialized in its declaration
|
||||
* @return true if variable declaration contains initialization
|
||||
*/
|
||||
bool isInit() const {
|
||||
return getFlag(fIsInit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Type pointer of known type.
|
||||
* @return pointer to type if known, NULL if not known
|
||||
|
|
18
lib/token.h
18
lib/token.h
|
@ -612,11 +612,18 @@ public:
|
|||
setFlag(fExternC, b);
|
||||
}
|
||||
|
||||
bool isSplittedVarDecl() const {
|
||||
return getFlag(fIsSplitVarDecl);
|
||||
bool isSplittedVarDeclComma() const {
|
||||
return getFlag(fIsSplitVarDeclComma);
|
||||
}
|
||||
void isSplittedVarDecl(bool b) {
|
||||
setFlag(fIsSplitVarDecl, b);
|
||||
void isSplittedVarDeclComma(bool b) {
|
||||
setFlag(fIsSplitVarDeclComma, b);
|
||||
}
|
||||
|
||||
bool isSplittedVarDeclEq() const {
|
||||
return getFlag(fIsSplitVarDeclEq);
|
||||
}
|
||||
void isSplittedVarDeclEq(bool b) {
|
||||
setFlag(fIsSplitVarDeclEq, b);
|
||||
}
|
||||
|
||||
bool isBitfield() const {
|
||||
|
@ -1201,7 +1208,8 @@ private:
|
|||
fIncompleteVar = (1 << 26),
|
||||
fConstexpr = (1 << 27),
|
||||
fExternC = (1 << 28),
|
||||
fIsSplitVarDecl = (1 << 29), // int a,b; <-- vardecl is split up
|
||||
fIsSplitVarDeclComma = (1 << 29), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;')
|
||||
fIsSplitVarDeclEq = (1 << 30) // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;')
|
||||
};
|
||||
|
||||
Token::Type mTokType;
|
||||
|
|
|
@ -4974,8 +4974,10 @@ void Tokenizer::dump(std::ostream &out) const
|
|||
}
|
||||
if (tok->isExpandedMacro())
|
||||
out << " isExpandedMacro=\"true\"";
|
||||
if (tok->isSplittedVarDecl())
|
||||
out << " isSplittedVarDecl=\"true\"";
|
||||
if (tok->isSplittedVarDeclComma())
|
||||
out << " isSplittedVarDeclComma=\"true\"";
|
||||
if (tok->isSplittedVarDeclEq())
|
||||
out << " isSplittedVarDeclEq=\"true\"";
|
||||
if (tok->link())
|
||||
out << " link=\"" << tok->link() << '\"';
|
||||
if (tok->varId() > 0)
|
||||
|
@ -6859,7 +6861,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
|
|||
|
||||
if (tok2->str() == ",") {
|
||||
tok2->str(";");
|
||||
tok2->isSplittedVarDecl(true);
|
||||
tok2->isSplittedVarDeclComma(true);
|
||||
//TODO: should we have to add also template '<>' links?
|
||||
TokenList::insertTokens(tok2, type0, typelen);
|
||||
}
|
||||
|
@ -6883,11 +6885,12 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co
|
|||
syntaxError(tok2); // invalid code
|
||||
TokenList::insertTokens(eq, varTok, 2);
|
||||
eq->str(";");
|
||||
eq->isSplittedVarDeclEq(true);
|
||||
|
||||
// "= x, " => "= x; type "
|
||||
if (tok2->str() == ",") {
|
||||
tok2->str(";");
|
||||
tok2->isSplittedVarDecl(true);
|
||||
tok2->isSplittedVarDeclComma(true);
|
||||
TokenList::insertTokens(tok2, type0, typelen);
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -3062,7 +3062,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, bool escape, Valu
|
|||
return std::vector<LifetimeToken> {};
|
||||
const Token* argTok = args[n];
|
||||
lt.errorPath.emplace_back(returnTok, "Return reference.");
|
||||
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->str() + "'.");
|
||||
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
|
||||
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
|
||||
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), depth - 1), returns.size() > 1);
|
||||
result.insert(result.end(), arglts.begin(), arglts.end());
|
||||
|
@ -3422,7 +3422,7 @@ struct LifetimeStore {
|
|||
return LifetimeStore{};
|
||||
}
|
||||
const Token *argtok2 = args[n];
|
||||
return LifetimeStore{argtok2, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object};
|
||||
return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object};
|
||||
}
|
||||
|
||||
template <class Predicate>
|
||||
|
@ -5650,10 +5650,8 @@ static void valueFlowUninit(TokenList *tokenlist, SymbolDatabase * /*symbolDatab
|
|||
// continue;
|
||||
if (!Token::Match(vardecl, "%var% ;"))
|
||||
continue;
|
||||
if (Token::Match(vardecl, "%varid% ; %varid% =", vardecl->varId()))
|
||||
continue;
|
||||
const Variable *var = vardecl->variable();
|
||||
if (!var || var->nameToken() != vardecl)
|
||||
if (!var || var->nameToken() != vardecl || var->isInit())
|
||||
continue;
|
||||
if ((!var->isPointer() && var->type() && var->type()->needInitialization != Type::NeedInitialization::True) ||
|
||||
!var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow())
|
||||
|
@ -5957,47 +5955,55 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
|
|||
continue;
|
||||
if (!tok->scope()->isExecutable())
|
||||
continue;
|
||||
if (!tok->variable())
|
||||
continue;
|
||||
const Variable * var = tok->variable();
|
||||
if (!var->isSmartPointer())
|
||||
continue;
|
||||
if (var->nameToken() == tok) {
|
||||
if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && tok->next()->astOperand2()->str() != ",") {
|
||||
Token * inTok = tok->next()->astOperand2();
|
||||
std::list<ValueFlow::Value> values = inTok->values();
|
||||
const bool constValue = inTok->isNumber();
|
||||
valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings);
|
||||
if (tok->variable()) {
|
||||
const Variable* var = tok->variable();
|
||||
if (!var->isSmartPointer())
|
||||
continue;
|
||||
if (var->nameToken() == tok) {
|
||||
if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() &&
|
||||
tok->next()->astOperand2()->str() != ",") {
|
||||
Token* inTok = tok->next()->astOperand2();
|
||||
std::list<ValueFlow::Value> values = inTok->values();
|
||||
const bool constValue = inTok->isNumber();
|
||||
valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings);
|
||||
|
||||
} else if (Token::Match(tok, "%var% ;")) {
|
||||
std::list<ValueFlow::Value> values;
|
||||
ValueFlow::Value v(0);
|
||||
v.setKnown();
|
||||
values.push_back(v);
|
||||
valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings);
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") {
|
||||
if (Token::simpleMatch(tok->tokAt(3), "( )")) {
|
||||
} else if (Token::Match(tok, "%var% ;")) {
|
||||
std::list<ValueFlow::Value> values;
|
||||
ValueFlow::Value v(0);
|
||||
v.setKnown();
|
||||
values.push_back(v);
|
||||
valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings);
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") {
|
||||
if (Token::simpleMatch(tok->tokAt(3), "( )")) {
|
||||
std::list<ValueFlow::Value> values;
|
||||
ValueFlow::Value v(0);
|
||||
v.setKnown();
|
||||
values.push_back(v);
|
||||
valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings);
|
||||
} else {
|
||||
tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue));
|
||||
Token* inTok = tok->tokAt(3)->astOperand2();
|
||||
if (!inTok)
|
||||
continue;
|
||||
std::list<ValueFlow::Value> values = inTok->values();
|
||||
const bool constValue = inTok->isNumber();
|
||||
valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings);
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") {
|
||||
std::list<ValueFlow::Value> values;
|
||||
ValueFlow::Value v(0);
|
||||
v.setKnown();
|
||||
values.push_back(v);
|
||||
valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings);
|
||||
} else {
|
||||
tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue));
|
||||
Token * inTok = tok->tokAt(3)->astOperand2();
|
||||
if (!inTok)
|
||||
continue;
|
||||
std::list<ValueFlow::Value> values = inTok->values();
|
||||
const bool constValue = inTok->isNumber();
|
||||
valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings);
|
||||
}
|
||||
} else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") {
|
||||
std::list<ValueFlow::Value> values;
|
||||
ValueFlow::Value v(0);
|
||||
v.setKnown();
|
||||
values.push_back(v);
|
||||
valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings);
|
||||
} else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) &&
|
||||
astIsSmartPointer(tok->astOperand1())) {
|
||||
std::vector<const Token*> args = getArguments(tok);
|
||||
if (args.empty())
|
||||
continue;
|
||||
for (const ValueFlow::Value& v : args.front()->values())
|
||||
setTokenValue(tok, v, settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6686,7 +6692,6 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
valueFlowGlobalStaticVar(tokenlist, settings);
|
||||
valueFlowPointerAlias(tokenlist);
|
||||
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowFunctionReturn(tokenlist, errorLogger);
|
||||
valueFlowBitAnd(tokenlist);
|
||||
valueFlowSameExpressions(tokenlist);
|
||||
valueFlowFwdAnalysis(tokenlist, settings);
|
||||
|
@ -6708,6 +6713,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase,
|
|||
valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowFunctionReturn(tokenlist, errorLogger);
|
||||
valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings);
|
||||
valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings);
|
||||
|
|
|
@ -1442,7 +1442,7 @@ private:
|
|||
check("auto& f() {\n"
|
||||
" return std::vector<int>{1}.front();\n"
|
||||
"}\n");
|
||||
TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", "", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) Reference to temporary returned.\n", errout.str());
|
||||
|
||||
check("struct A { int foo; };\n"
|
||||
"int& f(std::vector<A> v) {\n"
|
||||
|
@ -1638,7 +1638,8 @@ private:
|
|||
" int& x = h();\n"
|
||||
" g(&x);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n", errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n"
|
||||
"[test.cpp:4] -> [test.cpp:5]: (error) Using reference to dangling temporary.\n", errout.str());
|
||||
|
||||
check("void g(int*);\n"
|
||||
"int h();\n"
|
||||
|
@ -1675,6 +1676,27 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void danglingTempReference() {
|
||||
check("const std::string& g(const std::string& str_cref) {\n"
|
||||
" return str_cref;\n"
|
||||
"}\n"
|
||||
"void f() {\n"
|
||||
" const auto& str_cref2 = g(std::string(\"hello\"));\n"
|
||||
" std::cout << str_cref2 << std::endl;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("error", errout.str());
|
||||
|
||||
// Lifetime extended
|
||||
check("std::string g(const std::string& str_cref) {\n"
|
||||
" return str_cref;\n"
|
||||
"}\n"
|
||||
"void f() {\n"
|
||||
" const auto& str_cref2 = g(std::string(\"hello\"));\n"
|
||||
" std::cout << str_cref2 << std::endl;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void testglobalnamespace() {
|
||||
check("class SharedPtrHolder\n"
|
||||
"{\n"
|
||||
|
|
|
@ -44,6 +44,7 @@ private:
|
|||
TEST_CASE(uninit_bailout);
|
||||
TEST_CASE(uninit_fp_smartptr);
|
||||
TEST_CASE(uninit_fp_struct);
|
||||
TEST_CASE(uninit_fp_struct_member_init_2);
|
||||
TEST_CASE(uninit_fp_template_var);
|
||||
TEST_CASE(ctu);
|
||||
#endif
|
||||
|
@ -202,6 +203,16 @@ private:
|
|||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void uninit_fp_struct_member_init_2() {
|
||||
check("struct A {\n"
|
||||
" int x {0}; int y {0};\n"
|
||||
"};\n"
|
||||
"void foo(const A& a) {\n"
|
||||
" bar(a);\n"
|
||||
"}");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void uninit_fp_template_var() {
|
||||
check("void foo() {\n"
|
||||
" X*x = DYNAMIC_CAST(X, p);\n"
|
||||
|
|
|
@ -6567,6 +6567,19 @@ private:
|
|||
" Foo m_i;\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkInitializationListUsage("class A {\n" // #9821 - delegate constructor
|
||||
"public:\n"
|
||||
" A() : st{} {}\n"
|
||||
"\n"
|
||||
" explicit A(const std::string &input): A() {\n"
|
||||
" st = input;\n"
|
||||
" }\n"
|
||||
"\n"
|
||||
"private:\n"
|
||||
" std::string st;\n"
|
||||
"};");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3658,6 +3658,15 @@ private:
|
|||
" if(b[1] == 2) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str());
|
||||
|
||||
// #9878
|
||||
check("void f(bool a, bool b) {\n"
|
||||
" if (a && b){;}\n"
|
||||
" else if (!a && b){;}\n"
|
||||
" else if (!a && !b){;}\n"
|
||||
" else {;}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void duplicateCondition() {
|
||||
|
|
|
@ -2859,6 +2859,16 @@ private:
|
|||
" return x.release();\n"
|
||||
"}\n", true);
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
// #9496
|
||||
check("std::shared_ptr<int> f() {\n"
|
||||
" return std::shared_ptr<int>(nullptr);\n"
|
||||
"}\n"
|
||||
"void g() {\n"
|
||||
" int a = *f();\n"
|
||||
"}\n",
|
||||
true);
|
||||
ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout.str());
|
||||
}
|
||||
|
||||
void functioncall() { // #3443 - function calls
|
||||
|
|
|
@ -5310,7 +5310,7 @@ private:
|
|||
check("void f(int* x, bool b) {\n"
|
||||
" if ((!x && b) || (x != 0 && b)) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:2]: (style) Opposite expression on both sides of '||'.\n", errout.str());
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
}
|
||||
|
||||
void oppositeExpression() {
|
||||
|
|
|
@ -1152,8 +1152,9 @@ private:
|
|||
|
||||
ASSERT_EQUALS(expected, tok(code, false));
|
||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
||||
"[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n"
|
||||
, errout.str());
|
||||
"[test.cpp:31]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n"
|
||||
"[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void simplifyTypedef36() {
|
||||
|
|
|
@ -347,6 +347,24 @@ private:
|
|||
" x[0] = 2;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
checkNormal("void f(bool b) {\n"
|
||||
" std::vector<int> v;\n"
|
||||
" if(v.at(b?42:0)) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS(
|
||||
"test.cpp:3:error:Out of bounds access in expression 'v.at(b?42:0)' because 'v' is empty and 'at' may be non-zero.\n",
|
||||
errout.str());
|
||||
|
||||
checkNormal("void f(std::vector<int> v, bool b){\n"
|
||||
" if (v.size() == 1)\n"
|
||||
" if(v.at(b?42:0)) {}\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS(
|
||||
"test.cpp:3:warning:Either the condition 'v.size()==1' is redundant or v size can be 1. Expression 'v.at' cause access out of bounds.\n"
|
||||
"test.cpp:2:note:condition 'v.size()==1'\n"
|
||||
"test.cpp:3:note:Access out of bounds\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void outOfBoundsIndexExpression() {
|
||||
|
|
Loading…
Reference in New Issue