Skip to content
2 changes: 2 additions & 0 deletions lib/checkother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
7 changes: 6 additions & 1 deletion lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down Expand Up @@ -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())
Expand Down
18 changes: 18 additions & 0 deletions test/testautovariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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"
Expand Down
11 changes: 11 additions & 0 deletions test/testother.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
87 changes: 87 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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<char>(32);\n", "("));
ASSERT_EQUALS("", typeOf("a = (unsigned x)0;\n", "("));
Expand Down
Loading