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
This commit is contained in:
chrchr-github 2023-04-01 09:38:40 +02:00 committed by GitHub
parent cb915085ea
commit 01a22159b8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 99 additions and 21 deletions

View File

@ -312,11 +312,11 @@ bool astIsRangeBasedForDecl(const Token* tok)
return Token::simpleMatch(tok->astParent(), ":") && Token::simpleMatch(tok->astParent()->astParent(), "("); 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) if (!expr)
return ""; return "";
std::pair<const Token*, const Token*> decl = Token::typeDecl(expr, /*pointedToType*/ true); std::pair<const Token*, const Token*> decl = Token::typeDecl(expr, pointedToType);
if (decl.first && decl.second) { if (decl.first && decl.second) {
std::string ret; std::string ret;
for (const Token *type = decl.first; Token::Match(type,"%name%|::") && type != decl.second; type = type->next()) { for (const Token *type = decl.first; Token::Match(type,"%name%|::") && type != decl.second; type = type->next()) {

View File

@ -167,7 +167,7 @@ bool astIsRangeBasedForDecl(const Token* tok);
* static const int int * static const int int
* std::vector<T> std::vector * std::vector<T> 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 */ /** 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); const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr);

View File

@ -978,7 +978,7 @@ std::string Library::getFunctionName(const Token *ftok, bool &error) const
return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error); return getFunctionName(ftok->astOperand1(),error) + "::" + getFunctionName(ftok->astOperand2(),error);
} }
if (ftok->str() == "." && ftok->astOperand1()) { if (ftok->str() == "." && ftok->astOperand1()) {
const std::string type = astCanonicalType(ftok->astOperand1()); const std::string type = astCanonicalType(ftok->astOperand1(), ftok->originalName() == "->");
if (type.empty()) { if (type.empty()) {
error = true; error = true;
return ""; return "";

View File

@ -1303,12 +1303,12 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers()
(Token::Match(tok->next()->link(), ") . %name% !!(") || (Token::Match(tok->next()->link(), ") . %name% !!(") ||
(Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) { (Token::Match(tok->next()->link(), ") [") && Token::Match(tok->next()->link()->next()->link(), "] . %name% !!(")))) {
const Type *type = tok->function()->retType; const Type *type = tok->function()->retType;
if (type) { Token* membertok;
Token *membertok;
if (tok->next()->link()->next()->str() == ".") if (tok->next()->link()->next()->str() == ".")
membertok = tok->next()->link()->next()->next(); membertok = tok->next()->link()->next()->next();
else else
membertok = tok->next()->link()->next()->link()->next()->next(); membertok = tok->next()->link()->next()->link()->next()->next();
if (type) {
const Variable *membervar = membertok->variable(); const Variable *membervar = membertok->variable();
if (!membervar) { if (!membervar) {
if (type->classScope) { if (type->classScope) {
@ -1316,6 +1316,15 @@ void SymbolDatabase::createSymbolDatabaseSetVariablePointers()
setMemberVar(membervar, membertok, tok->function()->retDef); 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() == "(" && 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); const Library::ArgumentChecks::IteratorInfo* info = mSettings.library.getArgIteratorInfo(tok->previous(), 1);
if (info && info->it) { if (info && info->it) {
const Token* contTok = args[0]; 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(); 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) { if (contTok && contTok->variable() && contTok->variable()->valueType() && contTok->variable()->valueType()->container) {
ValueType vt; ValueType vt;
vt.type = ValueType::Type::ITERATOR; vt.type = ValueType::Type::ITERATOR;
@ -7199,7 +7214,7 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to
if (tok->str() == "auto" && !tok->valueType()) { if (tok->str() == "auto" && !tok->valueType()) {
if (Token::Match(tok->next(), "%name% ; %name% = [") && isLambdaCaptureList(tok->tokAt(5))) if (Token::Match(tok->next(), "%name% ; %name% = [") && isLambdaCaptureList(tok->tokAt(5)))
continue; continue;
if (Token::Match(tok->next(), "%name% { [") && isLambdaCaptureList(tok->tokAt(3))) if (Token::Match(tok->next(), "%name% {|= [") && isLambdaCaptureList(tok->tokAt(3)))
continue; continue;
debugMessage(tok, "autoNoType", "auto token with no type."); debugMessage(tok, "autoNoType", "auto token with no type.");
} }

View File

@ -2257,6 +2257,16 @@ std::pair<const Token*, const Token*> Token::typeDecl(const Token* tok, bool poi
const Variable *var = tok->variable(); const Variable *var = tok->variable();
if (!var->typeStartToken() || !var->typeEndToken()) if (!var->typeStartToken() || !var->typeEndToken())
return {}; 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<const Token*, const Token*> result; std::pair<const Token*, const Token*> result;
if (Token::simpleMatch(var->typeStartToken(), "auto")) { if (Token::simpleMatch(var->typeStartToken(), "auto")) {
const Token * tok2 = var->declEndToken(); const Token * tok2 = var->declEndToken();
@ -2306,16 +2316,6 @@ std::pair<const Token*, const Token*> Token::typeDecl(const Token* tok, bool poi
return { vt->containerTypeToken, vt->containerTypeToken->linkAt(-1) }; 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) if (result.first)
return result; return result;
return {var->typeStartToken(), var->typeEndToken()->next()}; return {var->typeStartToken(), var->typeEndToken()->next()};

View File

@ -2003,6 +2003,18 @@ private:
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("struct T;\n"
"std::shared_ptr<T> 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; settings = settings_old;
} }

View File

@ -8241,6 +8241,57 @@ private:
const Type* smpType = tok->valueType()->smartPointerType; const Type* smpType = tok->valueType()->smartPointerType;
ASSERT(smpType && smpType == tok->scope()->functionOf->definedType); ASSERT(smpType && smpType == tok->scope()->functionOf->definedType);
} }
{
GET_SYMBOL_DB("struct S { int i; };\n"
"void f(const std::vector<S>& 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<int> 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<int> 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<std::string> s; };\n"
"struct U { std::shared_ptr<T> 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() { void valueTypeThis() {