From 01a22159b8c1d6bb08335ed16f22102c67b4a106 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Sat, 1 Apr 2023 09:38:40 +0200 Subject: [PATCH] Fix autoNoType with std::begin() / function returning smart ptr (#4919) * Fix autoNoType with std::begin() * Suppress warning for const lambda * Fix autoNoType with function returning smart ptr * Handle more complex expression * Fix scope with auto and smart ptr * Handle smart pointers and iterators first --- lib/astutils.cpp | 4 +-- lib/astutils.h | 2 +- lib/library.cpp | 2 +- lib/symboldatabase.cpp | 29 ++++++++++++++++----- lib/token.cpp | 20 +++++++-------- test/testfunctions.cpp | 12 +++++++++ test/testsymboldatabase.cpp | 51 +++++++++++++++++++++++++++++++++++++ 7 files changed, 99 insertions(+), 21 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 21e9aa603..88500158d 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -312,11 +312,11 @@ bool astIsRangeBasedForDecl(const Token* tok) return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "("); } -std::string astCanonicalType(const Token *expr) +std::string astCanonicalType(const Token *expr, bool pointedToType) { if (!expr) return ""; - std::pair decl = Token::typeDecl(expr, /*pointedToType*/ true); + std::pair decl = Token::typeDecl(expr, pointedToType); if (decl.first && decl.second) { std::string ret; for (const Token *type = decl.first; Token::Match(type,"%name%|::") && type != decl.second; type = type->next()) { diff --git a/lib/astutils.h b/lib/astutils.h index b188e34f7..463141520 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -167,7 +167,7 @@ bool astIsRangeBasedForDecl(const Token* tok); * static const int int * std::vector std::vector */ -std::string astCanonicalType(const Token *expr); +std::string astCanonicalType(const Token *expr, bool pointedToType); /** Is given syntax tree a variable comparison against value */ const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr); diff --git a/lib/library.cpp b/lib/library.cpp index 007834802..e5b08e463 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -978,7 +978,7 @@ std::string Library::getFunctionName(const Token *ftok, bool &error) const return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); } if (ftok->str() == "." && ftok->astOperand1()) { - const std::string type = astCanonicalType(ftok->astOperand1()); + const std::string type = astCanonicalType(ftok->astOperand1(), ftok->originalName() == "->"); if (type.empty()) { error = true; return ""; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 6b631b5ae..f52ca9b4f 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -1303,12 +1303,12 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers() (Token::Match(tok->next()->link(), ") . %name% !!(") || (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { const Type *type = tok->function()->retType; + Token* membertok; + if (tok->next()->link()->next()->str() == ".") + membertok = tok->next()->link()->next()->next(); + else + membertok = tok->next()->link()->next()->link()->next()->next(); if (type) { - Token *membertok; - if (tok->next()->link()->next()->str() == ".") - membertok = tok->next()->link()->next()->next(); - else - membertok = tok->next()->link()->next()->link()->next()->next(); const Variable *membervar = membertok->variable(); if (!membervar) { if (type->classScope) { @@ -1316,6 +1316,15 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers() setMemberVar(membervar, membertok, tok->function()->retDef); } } + } else if (mSettings.library.detectSmartPointer(tok->function()->retDef)) { + if (const Token* templateArg = Token::findsimplematch(tok->function()->retDef, "<")) { + if (const Type* spType = findTypeInNested(templateArg->next(), tok->scope())) { + if (spType->classScope) { + const Variable* membervar = spType->classScope->getVariable(membertok->str()); + setMemberVar(membervar, membertok, tok->function()->retDef); + } + } + } } } else if (Token::simpleMatch(tok->astParent(), ".") && tok->next()->str() == "(" && @@ -7083,8 +7092,14 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to const Library::ArgumentChecks::IteratorInfo* info = mSettings.library.getArgIteratorInfo(tok->previous(), 1); if (info && info->it) { const Token* contTok = args[0]; - if (Token::simpleMatch(args[0]->astOperand1(), ".") && args[0]->astOperand1()->astOperand1()) + if (Token::simpleMatch(args[0]->astOperand1(), ".") && args[0]->astOperand1()->astOperand1()) // .begin() contTok = args[0]->astOperand1()->astOperand1(); + else if (Token::simpleMatch(args[0], "(") && args[0]->astOperand2()) // std::begin() + contTok = args[0]->astOperand2(); + while (Token::simpleMatch(contTok, "[")) // move to container token + contTok = contTok->astOperand1(); + if (Token::simpleMatch(contTok, ".")) + contTok = contTok->astOperand2(); if (contTok && contTok->variable() && contTok->variable()->valueType() && contTok->variable()->valueType()->container) { ValueType vt; vt.type = ValueType::Type::ITERATOR; @@ -7199,7 +7214,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to if (tok->str() == "auto" && !tok->valueType()) { if (Token::Match(tok->next(), "%name% ; %name% = [") && isLambdaCaptureList(tok->tokAt(5))) continue; - if (Token::Match(tok->next(), "%name% { [") && isLambdaCaptureList(tok->tokAt(3))) + if (Token::Match(tok->next(), "%name% {|= [") && isLambdaCaptureList(tok->tokAt(3))) continue; debugMessage(tok, "autoNoType", "auto token with no type."); } diff --git a/lib/token.cpp b/lib/token.cpp index 01a65e2c5..04376b6ac 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -2257,6 +2257,16 @@ std::pair Token::typeDecl(const Token* tok, bool poi const Variable *var = tok->variable(); if (!var->typeStartToken() || !var->typeEndToken()) return {}; + if (pointedToType && astIsSmartPointer(var->nameToken())) { + const ValueType* vt = var->valueType(); + if (vt && vt->smartPointerTypeToken) + return { vt->smartPointerTypeToken, vt->smartPointerTypeToken->linkAt(-1) }; + } + if (pointedToType && astIsIterator(var->nameToken())) { + const ValueType* vt = var->valueType(); + if (vt && vt->containerTypeToken) + return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; + } std::pair result; if (Token::simpleMatch(var->typeStartToken(), "auto")) { const Token * tok2 = var->declEndToken(); @@ -2306,16 +2316,6 @@ std::pair Token::typeDecl(const Token* tok, bool poi return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; } } - if (pointedToType && astIsSmartPointer(var->nameToken())) { - const ValueType* vt = var->valueType(); - if (vt && vt->smartPointerTypeToken) - return { vt->smartPointerTypeToken, vt->smartPointerTypeToken->linkAt(-1) }; - } - if (pointedToType && astIsIterator(var->nameToken())) { - const ValueType* vt = var->valueType(); - if (vt && vt->containerTypeToken) - return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; - } if (result.first) return result; return {var->typeStartToken(), var->typeEndToken()->next()}; diff --git a/test/testfunctions.cpp b/test/testfunctions.cpp index e63cf7680..5952fc795 100644 --- a/test/testfunctions.cpp +++ b/test/testfunctions.cpp @@ -2003,6 +2003,18 @@ private: "}\n"); ASSERT_EQUALS("", errout.str()); + check("struct T;\n" + "std::shared_ptr get();\n" + "void f(int i) {\n" + " auto p = get();\n" + " p->h(i);\n" + " p.reset(nullptr);\n" + "}\n"); + TODO_ASSERT_EQUALS("[test.cpp:5]: (information) --check-library: There is no matching configuration for function T::h()\n", + "[test.cpp:5]: (information) --check-library: There is no matching configuration for function T::h()\n" + "[test.cpp:6]: (information) --check-library: There is no matching configuration for function std::shared_ptr::reset()\n", + errout.str()); + settings = settings_old; } diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index fb7f44315..bf0bba47a 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -8241,6 +8241,57 @@ private: const Type* smpType = tok->valueType()->smartPointerType; ASSERT(smpType && smpType == tok->scope()->functionOf->definedType); } + { + GET_SYMBOL_DB("struct S { int i; };\n" + "void f(const std::vector& v) {\n" + " auto it = std::find_if(std::begin(v), std::end(v), [](const S& s) { return s.i != 0; });\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + const Token* tok = tokenizer.tokens(); + tok = Token::findsimplematch(tok, "auto"); + ASSERT(tok && tok->valueType()); + ASSERT_EQUALS("iterator(std :: vector <)", tok->valueType()->str()); + } + { + GET_SYMBOL_DB("struct S { std::vector v; };\n" + "struct T { S s; };\n" + "void f(T* t) {\n" + " auto it = std::find(t->s.v.begin(), t->s.v.end(), 0);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + const Token* tok = tokenizer.tokens(); + tok = Token::findsimplematch(tok, "auto"); + ASSERT(tok && tok->valueType()); + ASSERT_EQUALS("iterator(std :: vector <)", tok->valueType()->str()); + } + { + GET_SYMBOL_DB("struct S { std::vector v[1][1]; };\n" + "struct T { S s[1]; };\n" + "void f(T * t) {\n" + " auto it = std::find(t->s[0].v[0][0].begin(), t->s[0].v[0][0].end(), 0);\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + const Token* tok = tokenizer.tokens(); + tok = Token::findsimplematch(tok, "auto"); + ASSERT(tok && tok->valueType()); + ASSERT_EQUALS("iterator(std :: vector <)", tok->valueType()->str()); + } + { + GET_SYMBOL_DB("struct T { std::set s; };\n" + "struct U { std::shared_ptr get(); };\n" + "void f(U* u) {\n" + " for (const auto& str : u->get()->s) {}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + + const Token* tok = tokenizer.tokens(); + tok = Token::findsimplematch(tok, "auto"); + ASSERT(tok && tok->valueType()); + ASSERT_EQUALS("container(std :: string|wstring|u16string|u32string)", tok->valueType()->str()); + } } void valueTypeThis() {