Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/symboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() == "(" && 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 Expand Up @@ -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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reset is only reached for ( *. For a pointer to member function returning a reference, e.g. int& (S::*pm)();, the loop breaks at if (!Token::simpleMatch(type, "( *")) break; first, so valuetype->reference probably stays LValue. Meanwhile Variable::evaluate() resets on any (, so isReference() would be false. That makes Variable and ValueType disagree. Consider resetting the reference before the ( * check (whenever type is ( and we're stopping on a declarator), or handling ( %name% :: * as well. A test for int& (S::*pm)() would cover this.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This reset only runs for ( *. For a pointer to member function like int& (S::*pm)() the loop hits ( S and breaks at the if (!Token::simpleMatch(type, "( *")) break; above, before this point. Reference::LValue from the & is still set at that point, and the function returns a non-null type, so as far as I can tell pm's ValueType keeps reference == LValue. The new test ASSERT(p->valueType()->reference == Reference::None) for pm would then fail, or it passes only because the ValueType comes from somewhere I haven't found. Could you check this? One option is to clear the reference before the break whenever type->link() is followed by (, so that int& (S::*pm)() is covered too.

}
if (Token::simpleMatch(type, "decltype (") && type->next()->valueType()) {
const ValueType *vt2 = type->next()->valueType();
Expand Down
55 changes: 55 additions & 0 deletions test/testsymboldatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,61 @@ 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);
}
{ // 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);
}
{ // 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);
}
}

void VariableValueTypeTemplate() {
Expand Down
Loading