Skip to content

Commit 436735e

Browse files
committed
Fix issue with functions pointers
1 parent e521191 commit 436735e

7 files changed

Lines changed: 75 additions & 15 deletions

File tree

‎lib/checkio.cpp‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1554,7 +1554,7 @@ CheckIOImpl::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings &setti
15541554
while (top->astParent() && top->astParent()->str() != "," && top->astParent() != arg->previous())
15551555
top = top->astParent();
15561556
const ValueType *valuetype = top->argumentType();
1557-
if (valuetype && valuetype->type >= ValueType::Type::BOOL) {
1557+
if (valuetype && valuetype->isPrimitive()) {
15581558
typeToken = tempToken = new Token(top);
15591559
if (valuetype->pointer && valuetype->constness & 1) {
15601560
tempToken->str("const");
@@ -1583,7 +1583,10 @@ CheckIOImpl::ArgumentInfo::ArgumentInfo(const Token * arg, const Settings &setti
15831583
else if (valuetype->type == ValueType::LONGDOUBLE) {
15841584
tempToken->str("double");
15851585
tempToken->isLong(true);
1586-
}
1586+
} else if (valuetype->type == ValueType::FUNCTION)
1587+
tempToken->str("function");
1588+
else if (valuetype->type == ValueType::MEMBER_POINTER)
1589+
tempToken->str("member-pointer");
15871590
if (valuetype->isIntegral()) {
15881591
if (valuetype->sign == ValueType::Sign::UNSIGNED)
15891592
tempToken->isUnsigned(true);

‎lib/symboldatabase.cpp‎

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7363,13 +7363,19 @@ static ValueType memberPointerType(const ValueType& member)
73637363
}
73647364

73657365
// The "(" that opens the parenthesized declarator of a pointer or reference to a
7366-
// function ("int ( * f ) ( int )"), nullptr when var is not declared that way
7367-
static const Token* functionDeclarator(const Variable& var)
7366+
// function in the declaration whose return type starts at the given token
7367+
// ("int ( * f ) ( int )"), nullptr when there is no such declarator
7368+
static const Token* functionDeclarator(const Token* returnType)
73687369
{
7369-
const Token* tok = var.nameToken() ? var.nameToken()->previous() : nullptr;
7370-
while (Token::Match(tok, "*|&|&&|const|volatile"))
7371-
tok = tok->previous();
7372-
return Token::simpleMatch(tok, "(") ? tok : nullptr;
7370+
for (const Token* tok = returnType; tok; tok = tok->next()) {
7371+
if (Token::Match(tok, "( *|&|&&") && Token::simpleMatch(tok->link(), ") ("))
7372+
return tok;
7373+
if (tok->str() == "<" && tok->link())
7374+
tok = tok->link();
7375+
else if (!Token::Match(tok, "%name%|*|&|&&|::"))
7376+
return nullptr;
7377+
}
7378+
return nullptr;
73737379
}
73747380

73757381
void SymbolDatabase::setValueType(Token* tok, const ValueType& valuetype, const SourceLocation &loc)
@@ -7509,12 +7515,11 @@ void SymbolDatabase::setValueType(Token* tok, const ValueType& valuetype, const
75097515
}
75107516
// a call through a pointer or reference to a function has the return type of the
75117517
// function: the declared type before the parenthesized declarator "int ( * f ) ( int )"
7512-
if (Token::Match(parent->tokAt(-1), "%name% (") && parent->astOperand1() == tok && valuetype.type == ValueType::Type::FUNCTION &&
7513-
valuetype.pointer <= 1 && tok->variable()) {
7514-
const Variable* const var = tok->variable();
7515-
const Token* const declarator = functionDeclarator(*var);
7518+
if (parent->str() == "(" && parent->astOperand1() == tok && !parent->isCast() && valuetype.type == ValueType::Type::FUNCTION &&
7519+
valuetype.pointer <= 1 && valuetype.returnTypeToken) {
7520+
const Token* const declarator = functionDeclarator(valuetype.returnTypeToken);
75167521
ValueType returnType;
7517-
if (declarator && parsedecl(var->typeStartToken(), &returnType, mDefaultSignedness, mSettings, declarator))
7522+
if (declarator && parsedecl(valuetype.returnTypeToken, &returnType, mDefaultSignedness, mSettings, declarator))
75187523
setValueType(parent, returnType);
75197524
return;
75207525
}
@@ -7972,6 +7977,7 @@ static const Token* parsedecl(const Token* type,
79727977
const int pointer0 = valuetype->pointer;
79737978
while (Token::Match(type->previous(), "%name%") && !endsWith(type->strAt(-1), ':'))
79747979
type = type->previous();
7980+
const Token* const typeStart = type;
79757981
valuetype->sign = ValueType::Sign::UNKNOWN_SIGN;
79767982
if (!valuetype->typeScope && !valuetype->smartPointerType)
79777983
valuetype->type = ValueType::Type::UNKNOWN_TYPE;
@@ -8008,6 +8014,7 @@ static const Token* parsedecl(const Token* type,
80088014
function.type = ValueType::Type::FUNCTION;
80098015
function.bits = valuetype->bits;
80108016
function.pointer = pointer0;
8017+
function.returnTypeToken = typeStart;
80118018
if (type->strAt(1) == "&")
80128019
function.reference = Reference::LValue;
80138020
else if (type->strAt(1) == "&&")
@@ -8032,6 +8039,7 @@ static const Token* parsedecl(const Token* type,
80328039
valuetype->constness += vt2->constness;
80338040
valuetype->pointer += vt2->pointer;
80348041
valuetype->reference = vt2->reference;
8042+
valuetype->returnTypeToken = vt2->returnTypeToken;
80358043
type = type->linkAt(1)->next();
80368044
continue;
80378045
}
@@ -8095,6 +8103,7 @@ static const Token* parsedecl(const Token* type,
80958103
valuetype->constness = vt->constness;
80968104
valuetype->volatileness = vt->volatileness;
80978105
valuetype->originalTypeName = vt->originalTypeName;
8106+
valuetype->returnTypeToken = vt->returnTypeToken;
80988107
const bool hasConst = Token::simpleMatch(type->previous(), "const");
80998108
const bool hasVolatile = Token::simpleMatch(type->previous(), "volatile");
81008109
while (Token::Match(type, "%name%|*|&|&&|::") && !type->variable()) {

‎lib/symboldatabase.h‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1230,6 +1230,9 @@ class CPPCHECKLIB Scope {
12301230
class CPPCHECKLIB ValueType {
12311231
public:
12321232
enum Sign : std::uint8_t { UNKNOWN_SIGN, SIGNED, UNSIGNED } sign = UNKNOWN_SIGN;
1233+
/** The order matters: the types before VOID are class types that may have user defined
1234+
* constructors, assignment or operators. VOID and the types after it are built in types
1235+
* with trivial copy and move, the ones from BOOL on are arithmetic (isIntegral, isFloat). */
12331236
enum Type : std::uint8_t {
12341237
UNKNOWN_TYPE,
12351238
POD,
@@ -1238,9 +1241,9 @@ class CPPCHECKLIB ValueType {
12381241
SMART_POINTER,
12391242
CONTAINER,
12401243
ITERATOR,
1244+
VOID,
12411245
FUNCTION, ///< a function; a pointer to it ("int (*)(int)") is counted in pointer
12421246
MEMBER_POINTER, ///< a pointer to a data member ("int S::*"), counted in pointer
1243-
VOID,
12441247
BOOL,
12451248
CHAR,
12461249
SHORT,
@@ -1267,6 +1270,8 @@ class CPPCHECKLIB ValueType {
12671270
///< container
12681271
const Token* containerTypeToken{}; ///< The container type token. the template argument token that defines the
12691272
///< container element type.
1273+
const Token* returnTypeToken{}; ///< FUNCTION: the first token of the return type in the declaration of the
1274+
///< function ("int ( * f ) ( int )"), the type of a call is parsed from it
12701275
std::string originalTypeName; ///< original type name as written in the source code. eg. this might be "uint8_t"
12711276
///< when type is CHAR.
12721277
ErrorPath debugPath; ///< debug path to the type
@@ -1306,8 +1311,9 @@ class CPPCHECKLIB ValueType {
13061311
static MatchResult matchParameter(const ValueType *call, const ValueType *func);
13071312
static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar);
13081313

1314+
/** a built in type that is not void: arithmetic (or a pointer to it), a pointer to a function or to a member */
13091315
bool isPrimitive() const {
1310-
return (type >= ValueType::Type::BOOL);
1316+
return (type >= ValueType::Type::BOOL || type == ValueType::Type::FUNCTION || type == ValueType::Type::MEMBER_POINTER);
13111317
}
13121318

13131319
bool isIntegral() const {

‎test/testio.cpp‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5001,6 +5001,21 @@ class TestIO : public TestFixture {
50015001
" printf(\"%i\", fp());\n"
50025002
"}\n");
50035003
ASSERT_EQUALS("", errout_str());
5004+
5005+
check("struct S { int a; };\n" // a pointer to a function or to a member is a pointer
5006+
"void test(int (*cb)(int), int S::*pm) {\n"
5007+
" auto acb = cb;\n"
5008+
" printf(\"%d %d %d %p %p\", cb, pm, acb, cb, acb);\n"
5009+
"}\n");
5010+
ASSERT_EQUALS("[test.cpp:4:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'function *'. [invalidPrintfArgType_sint]\n"
5011+
"[test.cpp:4:5]: (warning) %d in format string (no. 2) requires 'int' but the argument type is 'member-pointer *'. [invalidPrintfArgType_sint]\n"
5012+
"[test.cpp:4:5]: (warning) %d in format string (no. 3) requires 'int' but the argument type is 'function *'. [invalidPrintfArgType_sint]\n", errout_str());
5013+
5014+
check("void test(char* (*cs)(int)) {\n" // a call through a copy of the pointer has the return type of the function
5015+
" auto acs = cs;\n"
5016+
" printf(\"%d %s\", acs(1), (*cs)(2));\n"
5017+
"}\n");
5018+
ASSERT_EQUALS("[test.cpp:3:5]: (warning) %d in format string (no. 1) requires 'int' but the argument type is 'char *'. [invalidPrintfArgType_sint]\n", errout_str());
50045019
}
50055020

50065021
void testPrintf0WithSuffix() { // ticket #7069

‎test/testleakautovar.cpp‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,15 @@ class TestLeakAutoVar : public TestFixture {
712712
" p->f();\n"
713713
"}\n", dinit(CheckOptions, $.cpp = true));
714714
ASSERT_EQUALS("[test.cpp:9:1]: (error) Memory leak: p [memleak]\n", errout_str());
715+
716+
check("struct S { int (*cb)(int); };\n" // a pointer to a function or to a member is a primitive member
717+
"struct T { int S::*pm; };\n"
718+
"void f() {\n"
719+
" S* s = new S;\n"
720+
" T* t = new T;\n"
721+
"}\n", dinit(CheckOptions, $.cpp = true));
722+
ASSERT_EQUALS("[test.cpp:6:1]: (error) Memory leak: s [memleak]\n"
723+
"[test.cpp:6:1]: (error) Memory leak: t [memleak]\n", errout_str());
715724
}
716725

717726
void realloc1() {

‎test/testother.cpp‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13136,6 +13136,16 @@ class TestOther : public TestFixture {
1313613136
" g(*w);\n"
1313713137
"}\n");
1313813138
ASSERT_EQUALS("[test.cpp:5:10]: (style) Variable 'w' can be declared as pointer to const [constVariablePointer]\n", errout_str());
13139+
13140+
check("struct S { int a; };\n" // moving a pointer to a function or to a member copies it
13141+
"void h(int (*)(int));\n"
13142+
"int f(int (*cb)(int), const S& s, int S::*pm) {\n"
13143+
" int (*c)(int) = std::move(cb);\n"
13144+
" int S::*q = std::move(pm);\n"
13145+
" h(std::move(cb));\n"
13146+
" return cb(1) + c(2) + s.*pm + s.*q;\n"
13147+
"}\n");
13148+
ASSERT_EQUALS("", errout_str());
1313913149
}
1314013150

1314113151
void moveAndAddressOf() {

‎test/testsymboldatabase.cpp‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10739,6 +10739,8 @@ class TestSymbolDatabase : public TestFixture {
1073910739
" auto acb = cb; auto apm = &S::a;\n"
1074010740
" g(cb); g(rf); g(*pcb); g(cbs[0]); g(pa); g(pmf); g(pm); g(&S::a); g(&S::s); g(&s.a); g(p); g(acb); g(apm);\n"
1074110741
" g(cb(1)); g(rf(2)); g(pf(3));\n"
10742+
" g(acb(4)); g((*cb)(5)); g(cbs[0](6)); g((*pcb)(7));\n"
10743+
" decltype(cb) dcb = cb; g(dcb(8));\n"
1074210744
"}\n");
1074310745
ASSERT(db != nullptr);
1074410746
const Token* const tokens = tokenizer.tokens();
@@ -10759,6 +10761,12 @@ class TestSymbolDatabase : public TestFixture {
1075910761
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( cb ( 1 ) )"));
1076010762
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( rf ( 2 ) )"));
1076110763
ASSERT_EQUALS("signed int *", argumentType(tokens, "g ( pf ( 3 ) )"));
10764+
// also when the pointer is not the declared variable: a copy, a dereference, an element
10765+
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( acb ( 4 ) )"));
10766+
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( ( * cb ) ( 5 ) )"));
10767+
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( cbs [ 0 ] ( 6 ) )"));
10768+
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( ( * pcb ) ( 7 ) )"));
10769+
ASSERT_EQUALS("signed int", argumentType(tokens, "g ( dcb ( 8 ) )"));
1076210770
}
1076310771

1076410772
// the bit count of the ValueType of the first token after start that matches pattern:

0 commit comments

Comments
 (0)