Merge branch 'main' into lifetime-subfunction

This commit is contained in:
Paul Fultz II 2020-09-09 12:02:02 -05:00 committed by GitHub
commit 34d65e25d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 278 additions and 77 deletions

View File

@ -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))

View File

@ -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)

View File

@ -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>

View File

@ -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()
}) {

View File

@ -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;

View File

@ -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, "");

View File

@ -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);

View File

@ -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.

View File

@ -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(),

View File

@ -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,10 +81,29 @@ 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) {
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();
const Token *other = nullptr;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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,14 +5955,14 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
continue;
if (!tok->scope()->isExecutable())
continue;
if (!tok->variable())
continue;
const Variable * var = tok->variable();
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();
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);
@ -5985,7 +5983,7 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
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();
Token* inTok = tok->tokAt(3)->astOperand2();
if (!inTok)
continue;
std::list<ValueFlow::Value> values = inTok->values();
@ -5999,6 +5997,14 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge
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);

View File

@ -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"

View File

@ -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"

View File

@ -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());
}

View File

@ -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() {

View File

@ -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

View File

@ -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() {

View File

@ -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() {

View File

@ -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() {