diff --git a/lib/checkother.cpp b/lib/checkother.cpp index c229cfed789..53edbcb508d 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1768,6 +1768,8 @@ void CheckOtherImpl::checkConstVariable() continue; if (var->isConst()) continue; + if (Tokenizer::isFunctionPointer(var->nameToken())) // reference to function + continue; const Scope* scope = var->scope(); if (!scope) continue; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index c2ce2db812b..7c44fcd4746 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -916,7 +916,8 @@ void SymbolDatabase::createSymbolDatabaseCopyAndMoveConstructors() continue; const Variable* firstArg = func->getArgumentVar(0); - if (firstArg->type() == scope.definedType) { + // skip references to functions: Foo(Foo& (&f)()) + if (firstArg->type() == scope.definedType && !Token::Match(firstArg->typeEndToken(), "&|&& %name%| ) (")) { if (firstArg->isRValueReference()) func->type = FunctionType::eMoveConstructor; else if (firstArg->isReference() && !firstArg->isPointer()) @@ -2595,6 +2596,10 @@ void Variable::evaluate(const Settings& settings) } else if (tok->str() == "&&") { // Before simplification, && isn't split up setFlag(fIsRValueRef, true); setFlag(fIsReference, true); // Set also fIsReference + } else if (tok->str() == "(" && Token::simpleMatch(tok->link(), ") (")) { + // a reference before the parentheses belongs to the return type of a function pointer: int& (*f)() + setFlag(fIsRValueRef, false); + setFlag(fIsReference, false); } if (tok->str() == "<" && tok->link()) diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index d1bc3bf830f..a2fa4168ea8 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -133,6 +133,7 @@ class TestAutoVariables : public TestFixture { TEST_CASE(returnReference27); TEST_CASE(returnReference28); TEST_CASE(returnReference29); + TEST_CASE(returnReference30); // #15094 TEST_CASE(returnReferenceFunction); TEST_CASE(returnReferenceContainer); TEST_CASE(returnReferenceLiteral); @@ -1772,6 +1773,23 @@ class TestAutoVariables : public TestFixture { errout_str()); } + void returnReference30() // #15094 + { + check("int& f1(int& (*fp)()) { return fp(); }\n" + "void f2(int& (*fp)()) { int& r = fp(); r = 2; }\n" + "struct D {\n" + " int& call() { return f(); }\n" + " int& (*f)();\n" + "};\n" + "const int& f3(const int& (*fp)(int)) { return fp(1); }\n" + "int& (*g)();\n" + "int& f4() { return g(); }\n" + "typedef int& (*FP)();\n" + "int& f5(FP fp) { return fp(); }\n" + "int* f6(int& (*fp)()) { return &fp(); }\n"); + ASSERT_EQUALS("", errout_str()); + } + void returnReferenceFunction() { check("int& f(int& a) {\n" " return a;\n" diff --git a/test/testother.cpp b/test/testother.cpp index 62824c16b36..f07f68ed951 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -4213,6 +4213,17 @@ class TestOther : public TestFixture { " }\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("int& g();\n" // reference to function + "int k();\n" + "int f1() { int& (&r)() = g; return r(); }\n" + "int f2() { int (&r)() = k; return r(); }\n" + "int& f3(int& (&f)()) { return f(); }\n" + "int f4(int&& (&f)()) { return f(); }\n" + "int f5(int (&f)()) { return f(); }\n" + "using FR = int&(&)();\n" + "void f6(FR f) { f(); }\n"); + ASSERT_EQUALS("", errout_str()); } void constParameterCallback() { diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 93bc8861e3e..7447c687ff1 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -1602,6 +1602,66 @@ class TestSymbolDatabase : public TestFixture { ASSERT(p->valueType()->originalTypeName == "ubFunctionPointer_fp"); ASSERT(p->valueType()->reference == Reference::None); } + { // function pointer returning a reference + GET_SYMBOL_DB("void foo(int& (*f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(!p->isReference()); + ASSERT(p->isPointer()); + ASSERT(p->valueType()); + ASSERT(p->valueType()->pointer == 1); + } + { + GET_SYMBOL_DB("void foo(int&& (*f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(!p->isReference()); + ASSERT(!p->isRValueReference()); + } + { + GET_SYMBOL_DB("struct S { int& (*f[2])(); };\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(!p->isReference()); + ASSERT(p->isArray()); + } + { // pointer to member function returning a reference + GET_SYMBOL_DB("struct S { int& g(); };\n" + "void foo(int& (S::*pm)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(p); + ASSERT_EQUALS("pm", p->name()); + ASSERT(!p->isReference()); + } + { // parentheses that are no function declarator + GET_SYMBOL_DB("void foo(int& (r)) {}\n"); + const Variable* const p = db->functionScopes.front()->function->getArgumentVar(0); + ASSERT(p); + ASSERT(p->isReference()); + } + { + GET_SYMBOL_DB("void foo(int& UNUSED(r)) {}\n"); + const Variable* const p = db->functionScopes.front()->function->getArgumentVar(0); + ASSERT(p); + ASSERT(p->isReference()); + } + { // reference to function pointer + GET_SYMBOL_DB("void foo(int (*&f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(p->isReference()); + ASSERT(p->isPointer()); + ASSERT(p->valueType()); + ASSERT(p->valueType()->reference == Reference::LValue); + } + { // reference to function returning a reference + GET_SYMBOL_DB("void foo(int& (&f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(p->isReference()); + ASSERT(!p->isRValueReference()); + } + { + GET_SYMBOL_DB("void foo(int&& (&f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(p->isReference()); + ASSERT(!p->isRValueReference()); + } } void VariableValueTypeTemplate() { @@ -2410,6 +2470,31 @@ class TestSymbolDatabase : public TestFixture { ASSERT(db && ctor && ctor->type == FunctionType::eMoveConstructor); ASSERT(ctor && ctor->retDef == nullptr); } + { + GET_SYMBOL_DB("class Foo { Foo(Foo&& (*f)()); };\n"); + const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); + ASSERT(db && ctor && ctor->type == FunctionType::eConstructor); + } + { + GET_SYMBOL_DB("class Foo { Foo(Foo& (&f)()); };\n"); + const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); + ASSERT(db && ctor && ctor->type == FunctionType::eConstructor); + } + { + GET_SYMBOL_DB("class Foo { Foo(Foo&& (&f)()); };\n"); + const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); + ASSERT(db && ctor && ctor->type == FunctionType::eConstructor); + } + { + GET_SYMBOL_DB("class Foo { Foo(Foo (&f)()); };\n"); + const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); + ASSERT(db && ctor && ctor->type == FunctionType::eConstructor); + } + { + GET_SYMBOL_DB("class Foo { Foo(Foo& (&)()); };\n"); + const Function* ctor = tokenizer.tokens()->tokAt(3)->function(); + ASSERT(db && ctor && ctor->type == FunctionType::eConstructor); + } { GET_SYMBOL_DB("void f() { extern void f(); }\n"); ASSERT(db && db->scopeList.size() == 2); @@ -9855,6 +9940,8 @@ class TestSymbolDatabase : public TestFixture { ASSERT_EQUALS("signed long", typeOf("a = (long)32;\n", "(")); ASSERT_EQUALS("signed long", typeOf("a = (long int)32;\n", "(")); ASSERT_EQUALS("signed long long", typeOf("a = (long long)32;\n", "(")); + ASSERT_EQUALS("signed int &", typeOf("void f(int& (*fp)()) { fp(); }\n", "( ) ;")); // #15094 + ASSERT_EQUALS("signed int &&", typeOf("void f(int&& (*fp)()) { fp(); }\n", "( ) ;")); ASSERT_EQUALS("long double", typeOf("a = (long double)32;\n", "(")); ASSERT_EQUALS("char", typeOf("a = static_cast(32);\n", "(")); ASSERT_EQUALS("", typeOf("a = (unsigned x)0;\n", "("));