Protect against function-looking pointers inside struct definitions (Fix #399)

This commit is contained in:
Mark VanderVoord
2026-06-26 14:22:13 -04:00
parent 9e8d1f93ef
commit 55dd1f1ce5
2 changed files with 77 additions and 0 deletions
+2
View File
@@ -251,6 +251,8 @@ class CMockHeaderParser
source.gsub!(/^\s*#.*/, '')
# enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
# pre-collapse nested brace pairs so that structs containing nested structs/unions are removed as a unit below
source = remove_nested_pairs_of_braces(source) unless cpp
# forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
source.gsub!(/^[\w\s]*struct[^;{}()]+;/m, '') # remove forward declared structs
source.gsub!(/^[\w\s]*(enum|union|struct|typedef)[\w\s()]*\{[^}]+\}[\w\s*,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces
@@ -0,0 +1,75 @@
# =========================================================================
# CMock - Automatic Mock Generation for C
# ThrowTheSwitch.org
# Copyright (c) 2007-26 Mike Karlesky, Mark VanderVoord, & Greg Williams
# SPDX-License-Identifier: MIT
# =========================================================================
---
#The purpose of this test is to verify that structs containing function pointer
#members are not mistakenly mocked by CMock. Only actual function prototypes
#at file scope should be mocked. This is especially tricky when the struct
#contains nested anonymous structs or unions, because the struct removal regex
#can be confused by the inner closing brace and leave function pointer members
#in the source for the parser to encounter.
:cmock:
:plugins:
- # none
:includes:
- "<stdint.h>"
:systest:
:types: |
#include <stdint.h>
/* Forward-declare the type so the source header can use SaladBowl* before
the full definition (which lives in the mockable header) is visible. */
typedef struct SaladBowlStruct SaladBowl;
:mockable: |
#include <stdint.h>
/* Full struct definition with a nested anonymous struct and function pointer
members. CMock must ignore all of these and only mock saladBowlInit. */
struct SaladBowlStruct {
struct {
uint16_t remainingCapacity;
uint16_t ingredientCount;
} stats;
void* (*toss)(struct SaladBowlStruct *self, uint16_t itemSize);
int32_t (*empty)(struct SaladBowlStruct *self);
void* (*grab)(struct SaladBowlStruct *self, uint16_t itemIndex);
void* (*add)(struct SaladBowlStruct *self, uint16_t itemIndex, uint16_t itemSize);
int32_t (*pluck)(struct SaladBowlStruct *self, uint16_t itemIndex);
};
int32_t saladBowlInit(SaladBowl *bowl, uint16_t sizeInBytes, uint16_t headerSizeInBytes);
:source:
:header: |
#include <stdint.h>
void exercise_salad_bowl(SaladBowl *bowl);
:code: |
void exercise_salad_bowl(SaladBowl *bowl)
{
saladBowlInit(bowl, 256, 16);
}
:tests:
:common: |
SaladBowl g_bowl;
void setUp(void) {}
void tearDown(void) {}
:units:
- :pass: TRUE
:should: 'mock only saladBowlInit and ignore function pointer members of the struct'
:code: |
test()
{
saladBowlInit_ExpectAndReturn(&g_bowl, 256, 16, 0);
exercise_salad_bowl(&g_bowl);
}
...