Repository navigation
Description
Versions
CMock 2.7.2 at commit 34c26e5 "Merge pull request #540 from ThrowTheSwitch/feature/mock-name-include-folder" (the head of master today), as vendored in Ceedling 1.2.3.dev-462c48b. The compiler error shown below also appears with Ceedling 1.2.1-47d2c5e, which vendors CMock 2.7.2 as well.
What went wrong?
The mock of a function does not compile when the type that one of its pointer parameters points to is restrict-qualified, as in char *restrict *name. CMock writes the definition of the mocked function without that restrict, so this definition is no longer compatible with the declaration in the header that the mock includes.
C99 requires all the declarations of a function in one scope to have compatible types (C99 section 6.7, paragraph 4). When C99 compares the types of two declarations of a function, it ignores the qualifiers of each parameter itself (section 6.7.5.3, paragraph 15). C99 does not ignore a restrict that qualifies the type that a pointer parameter points to, since two pointer types are compatible only when both are identically qualified and point to compatible types (section 6.7.5.1, paragraph 2). So int f(char **restrict name); and int f(char **name); are compatible, while int f(char *restrict *name); and int f(char **name); are not.
The attached project reproduces the problem with one header and one test file. This is src/name_store.h:
#ifndef NAME_STORE_H
#define NAME_STORE_H
int name_store_get(char *restrict *name);
#endifThis is test/test_name_store.c:
#include "unity.h"
#include "Mockname_store.h"
void setUp(void) {}
void tearDown(void) {}
void test_name_store_get_is_mocked(void)
{
char *name = NULL;
name_store_get_ExpectAndReturn(&name, 0);
TEST_ASSERT_EQUAL_INT(0, name_store_get(&name));
}To reproduce the problem, unzip the attached project and run ceedling test:all in its cmock_restrict_pointee directory. The project file sets no :cmock: option, so CMock runs with the settings that Ceedling gives it by default. GCC fails to compile Mockname_store.c with the error and the note below. I left out the source lines that GCC quotes under each of them:
build/test/mocks/test/test_name_store/Mockname_store.c:53:5: error: conflicting types for 'name_store_get'; have 'int(char **)'
In file included from build/test/mocks/test/test_name_store/Mockname_store.h:6,
from build/test/mocks/test/test_name_store/Mockname_store.c:6:
src/name_store.h:4:5: note: previous declaration of 'name_store_get' with type 'int(char * restrict*)'
Just before the error, Ceedling prints a notice that says src/name_store.h also reaches compilation "through a second unmocked #include chain". That chain is Mockname_store.h including name_store.h, as the output above shows, and it is not the cause of the error.
Line 53 of Mockname_store.c is the definition that CMock writes:
int name_store_get(char** name)Mockname_store.c includes Mockname_store.h, which includes name_store.h, so the compiler sees both the declaration int name_store_get(char *restrict *name); and this definition. The definition lacks restrict because import_source() in lib/cmock_header_parser.rb removes the keyword from the text of the header before CMock parses the declarations (line 305 at commit 34c26e5).
With :treat_inlines: :include, CMock also writes a copy of name_store.h next to the mock, and the mock includes that copy instead of src/name_store.h. That copy still has the restrict, so the same error appears, and the note then names build/test/mocks/test/test_name_store/name_store.h.
Expected: the mock compiles. To check that a definition that keeps the restrict compiles, I changed line 53 of the generated Mockname_store.c by hand to int name_store_get(char *restrict *name), and ran by hand the compiler command that Ceedling printed with the error, with -Wall -Wextra added. The mock compiled without a warning.
Workaround: our firmware declares a function with the parameter char *restrict thread_name[restrict static 1]. The test builds of the firmware define restrict as empty, so that the preprocessor removes every restrict from the header and from the mock before the compiler compares the declaration with the definition. The Ceedling project file of the firmware does so with this entry:
:defines:
:test:
- restrict=Environment
Windows 11 Pro 10.0.26300, Ruby 3.4.9 (x64-mingw-ucrt), GCC 16.2.0 (MSYS2 UCRT64)
Project configuration
This is project.yml:
:project:
:build_root: build
:test_file_prefix: test_
:paths:
:test:
- test/**
:source:
- src/**
:include:
- src/**Anything else?
The attached project: cmock_restrict_pointee.zip
This issue, the attached project and the analysis of the files that CMock generates were written with Claude Code, using Claude Opus 5.5.
Activity