From 6b8f345d10601c69ff8ed06f20220aa8c30c8c6d Mon Sep 17 00:00:00 2001 From: Leander Schulten Date: Fri, 9 Oct 2026 16:20:10 +0200 Subject: [PATCH 1/3] Fix #15094 SymbolDatabase: Variable::isReference() is true for function pointer returning a reference For `int& (*f)()` the reference of the return type was taken as reference of the variable, in Variable::isReference() and in its ValueType. Co-Authored-By: Claude Opus 5.5 --- lib/symboldatabase.cpp | 6 ++++++ test/testsymboldatabase.cpp | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index c2ce2db812b..c67b4ce4714 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2595,6 +2595,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() == "(") { + // 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()) @@ -7578,6 +7582,8 @@ static const Token* parsedecl(const Token* type, if (par) break; par = true; + // a reference before the parentheses belongs to the return type of the function pointer + valuetype->reference = Reference::None; } if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) { const ValueType *vt2 = type->next()->valueType(); diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 93bc8861e3e..8c97405915b 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -1602,6 +1602,39 @@ 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); + ASSERT(p->valueType()->reference == Reference::None); + } + { + GET_SYMBOL_DB("void foo(int&& (*f)()) {}\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(!p->isReference()); + ASSERT(!p->isRValueReference()); + ASSERT(p->valueType()); + ASSERT(p->valueType()->reference == Reference::None); + } + { + GET_SYMBOL_DB("struct S { int& (*f[2])(); };\n"); + const Variable* const p = db->getVariableFromVarId(1); + ASSERT(!p->isReference()); + ASSERT(p->isArray()); + ASSERT(p->valueType()); + ASSERT(p->valueType()->reference == Reference::None); + } + { // 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); + } } void VariableValueTypeTemplate() { From 0fb00fbd5a641d2f038e6fc2ffc22c15343204f5 Mon Sep 17 00:00:00 2001 From: Leander Schulten Date: Fri, 9 Oct 2026 21:53:36 +0200 Subject: [PATCH 2/3] Add test for pointer to member function returning a reference Co-Authored-By: Claude Opus 5.5 --- test/testsymboldatabase.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 8c97405915b..224853cf5e7 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -1627,6 +1627,16 @@ class TestSymbolDatabase : public TestFixture { ASSERT(p->valueType()); ASSERT(p->valueType()->reference == Reference::None); } + { // 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()); + ASSERT(p->valueType()); + ASSERT(p->valueType()->reference == Reference::None); + } { // reference to function pointer GET_SYMBOL_DB("void foo(int (*&f)()) {}\n"); const Variable* const p = db->getVariableFromVarId(1); From da724f2e57007185f2db13d9252655017ada8eca Mon Sep 17 00:00:00 2001 From: Leander Schulten Date: Fri, 9 Oct 2026 22:01:23 +0200 Subject: [PATCH 3/3] Only reset reference at function declarator parentheses `int& (r)` and `int& UNUSED(r)` lost their reference flag, which gave uninitvar false positives. Co-Authored-By: Claude Opus 5.5 --- lib/symboldatabase.cpp | 2 +- test/testsymboldatabase.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index c67b4ce4714..06eda1d3b9f 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2595,7 +2595,7 @@ 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() == "(") { + } 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); diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index 224853cf5e7..76690d32336 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -1637,6 +1637,18 @@ class TestSymbolDatabase : public TestFixture { ASSERT(p->valueType()); ASSERT(p->valueType()->reference == Reference::None); } + { // 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);