From b044f9ba961524d998bf0af3bb5c5110b3c72add Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Fri, 18 Dec 2020 00:14:11 -0600 Subject: [PATCH] Fix issue 9996: false negative: containerOutOfBounds with std::vector::front() and c++11 braced initializer (#2958) --- lib/valueflow.cpp | 44 +++++++++++++++++++++++++++++++++++------- test/testvalueflow.cpp | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 7 deletions(-) diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index cc2a58485..78bc0ce6d 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -5970,6 +5970,24 @@ static void valueFlowIteratorInfer(TokenList *tokenlist, const Settings *setting } } +static std::vector getInitListSize(const Token* tok) +{ + std::vector args = getArguments(tok); + if ((args.size() == 1 && astIsContainer(args[0]) && args[0]->valueType()->container == tok->valueType()->container) || + (args.size() == 2 && astIsIterator(args[0]) && astIsIterator(args[1]))) { + std::vector values; + std::copy_if(args[0]->values().begin(), + args[0]->values().end(), + std::back_inserter(values), + std::mem_fn(&ValueFlow::Value::isContainerSizeValue)); + return values; + } + ValueFlow::Value value(args.size()); + value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + value.setKnown(); + return {value}; +} + static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger * /*errorLogger*/, const Settings *settings) { // declaration @@ -5978,22 +5996,27 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold continue; if (!var->valueType() || !var->valueType()->container) continue; - if (!Token::Match(var->nameToken(), "%name% ;")) - continue; if (!astIsContainer(var->nameToken())) continue; if (var->nameToken()->hasKnownValue()) continue; - ValueFlow::Value value(0); + if (!Token::Match(var->nameToken(), "%name% ;") && + !(Token::Match(var->nameToken(), "%name% {") && Token::simpleMatch(var->nameToken()->next()->link(), "} ;"))) + continue; + std::vector values{ValueFlow::Value{0}}; + values.back().valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; + values.back().setKnown(); if (var->valueType()->container->size_templateArgNo >= 0) { if (var->dimensions().size() == 1 && var->dimensions().front().known) - value.intvalue = var->dimensions().front().num; + values.back().intvalue = var->dimensions().front().num; else continue; + } else if (Token::simpleMatch(var->nameToken()->next(), "{")) { + const Token* initList = var->nameToken()->next(); + values = getInitListSize(initList); } - value.valueType = ValueFlow::Value::ValueType::CONTAINER_SIZE; - value.setKnown(); - valueFlowContainerForward(var->nameToken()->next(), var, value, tokenlist); + for (const ValueFlow::Value& value : values) + valueFlowContainerForward(var->nameToken()->next(), var, value, tokenlist); } // after assignment @@ -6007,6 +6030,13 @@ static void valueFlowContainerSize(TokenList *tokenlist, SymbolDatabase* symbold value.setKnown(); valueFlowContainerForward(containerTok->next(), containerTok->variable(), value, tokenlist); } + } else if (Token::Match(tok, "%name%|;|{|}|> %var% = {") && Token::simpleMatch(tok->linkAt(3), "} ;")) { + const Token* containerTok = tok->next(); + if (astIsContainer(containerTok)) { + std::vector values = getInitListSize(tok->tokAt(3)); + for (const ValueFlow::Value& value : values) + valueFlowContainerForward(containerTok->next(), containerTok->variable(), value, tokenlist); + } } else if (Token::Match(tok, "%var% . %name% (") && tok->valueType() && tok->valueType()->container) { Library::Container::Action action = tok->valueType()->container->getAction(tok->strAt(2)); if (action == Library::Container::Action::CLEAR) { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 038b43b8a..262210e9a 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -4703,6 +4703,42 @@ private: " }\n" "}\n"; ASSERT_EQUALS(0U, tokenValues(code, "str . front").size()); + + code = "void f() {\n" + " std::vector ints{};\n" + " ints.front();\n" + "}"; + ASSERT_EQUALS("", + isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 0)); + + code = "void f() {\n" + " std::vector ints{1};\n" + " ints.front();\n" + "}"; + ASSERT_EQUALS("", + isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 1)); + + code = "void f() {\n" + " std::vector ints{1};\n" + " std::vector ints2{ints.begin(), ints.end()};\n" + " ints2.front();\n" + "}"; + ASSERT_EQUALS( + "", isKnownContainerSizeValue(tokenValues(code, "ints2 . front", ValueFlow::Value::CONTAINER_SIZE), 1)); + + code = "void f() {\n" + " std::vector ints = {};\n" + " ints.front();\n" + "}"; + ASSERT_EQUALS("", + isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 0)); + + code = "void f() {\n" + " std::vector ints = {1};\n" + " ints.front();\n" + "}"; + ASSERT_EQUALS("", + isKnownContainerSizeValue(tokenValues(code, "ints . front", ValueFlow::Value::CONTAINER_SIZE), 1)); } void valueFlowDynamicBufferSize() {