doc: fix WARNING: Invalid definition" due to unamed structs/unions

Fix with a workaround in unnamed unions / structs in bluetooth, i2c,
sensor and uart.

Current documentation parsers (sphinx under Doxygen) don't seem to
understand well unnamed structs / unions. They will not generate any
documentation for any documented members (see left side of
http://imgur.com/mcpBXWc).

A workaround is to make the parser think there is something like a
struct/union/enum name that is actually something with no effect to
the compiler.

Naming it with __unnamed_workaround__ ensures it is clear it is a
workaround while we wait for a final fix. It is #defined to be a NO-OP
to the compiler and rearrange the member documentation as *@param* so
we have some documentation that the non-worked around code fails to
document.

Anonymous structs/union that declare a variable are just given an
internal name.

Workarounds documented in the contribution guidelines.

Change-Id: I4d32cf444f3c5e7d2fb11581e4b41f80e93c9786
Signed-off-by: Inaky Perez-Gonzalez <inaky.perez-gonzalez@intel.com>
This commit is contained in:
Inaky Perez-Gonzalez 2016-06-16 14:52:44 -07:00
commit ecc4c765cc
7 changed files with 185 additions and 30 deletions

View file

@ -27,6 +27,143 @@ Structs have a simplified template:
Doxygen does not require any commands to recognize the different comments. Doxygen does not require any commands to recognize the different comments.
It does, however, require that line 8 be left blank. It does, however, require that line 8 be left blank.
Unnamed structures or unions
****************************
The *Sphinx* parser seems to get confused by unnamed structures, used
specially in nested unions / structs:
.. code-block:: c
struct sensor_value {
enum sensor_value_type type;
union {
struct {
int32_t val1;
int32_t val2;
};
double dval;
};
};
This will likely generate an error such as::
doc/api/io_interfaces.rst:14: WARNING: Invalid definition: Expected identifier in nested name. [error at 0]
^
doc/api/io_interfaces.rst:14: WARNING: Invalid definition: Expected identifier in nested name. [error at 0]
^
doc/api/io_interfaces.rst:14: WARNING: Invalid definition: Expected end of definition. [error at 12]
sensor_value.__unnamed__
------------^
A workaround is to introduce something that looks like a name to the
parser but it is a no-op to the compiler:
.. code-block:: c
/**
* Workaround for documentation parser limitations
*
* Current documentation parsers (sphinx under Doxygen) don't seem to
* understand well unnamed structs / unions. A workaround is to make
* the parser think there is something like a struct/union/enum name
* that is actually something with no effect to the compiler.
*
* Current choice is to give it a 1B alignment. This basically tells
* the compiler to do what is doing now: align it wherever it thinks
* it should, as a 1B alignment "restriction" fits any other alignment
* restriction we might have.
*/
#define __unnamed_workaround__ __attribute__ ((__aligned__ (1)))
And then use it such as:
.. code-block:: c
struct sensor_value {
enum sensor_value_type type;
union __unnamed_workaround__ {
struct __unnamed_workaround__ {
int32_t val1;
int32_t val2;
};
double dval;
};
};
This is currently defined in :file:`include/toolchain.h`. The issue
reported to developers in
https://github.com/sphinx-doc/sphinx/issues/2683.
When running into this issue, the member documentation has to be done
with *@param* indicators, otherwise they won't be extracted:
.. code-block:: c
/**
* @brief UART device configuration.
*
* @param port Base port number
* @param base Memory mapped base address
* @param regs Register address
* @param sys_clk_freq System clock frequency in Hz
*/
struct uart_device_config {
union __unnamed_workaround__ {
uint32_t port;
uint8_t *base;
uint32_t regs;
};
uint32_t sys_clk_freq;
...
Unnamed structures or unions which declare a variable
-----------------------------------------------------
A special case of this is non-anynoymous unnamed structs/unions, where
a variable is defined. In this case, the workaround is much more
simple. Consider:
.. code-block:: c
union dev_config {
uint32_t raw;
struct {
uint32_t use_10_bit_addr : 1;
uint32_t speed : 3;
uint32_t is_master_device : 1;
uint32_t is_slave_read : 1;
uint32_t reserved : 26;
} bits;
};
This will likely generate an error such as::
doc/api/io_interfaces.rst:28: WARNING: Invalid definition: Expected identifier in nested name. [error at 19]
struct dev_config::@61 dev_config::bits
-------------------^
The simple solution is to name that unnamed struct, with an internal
name (such as *__bits*):
.. code-block:: c
union dev_config {
uint32_t raw;
struct __bits {
uint32_t use_10_bit_addr : 1;
uint32_t speed : 3;
uint32_t is_master_device : 1;
uint32_t is_slave_read : 1;
uint32_t reserved : 26;
} bits;
};
Structure Documentation Example Structure Documentation Example
******************************* *******************************
@ -56,4 +193,4 @@ Incorrect:
The struct has no documentation. Developers that want to expand its The struct has no documentation. Developers that want to expand its
functionality have no way of understanding why the struct is defined functionality have no way of understanding why the struct is defined
this way, nor what its components are. this way, nor what its components are.

View file

@ -135,19 +135,22 @@ enum {
BT_CONN_ROLE_SLAVE, BT_CONN_ROLE_SLAVE,
}; };
/** Connection Info Structure */ /** @brief Connection Info Structure
*
*
* @param type Connection Type
* @param role Connection Role
* @param le LE Connection specific Info
* @param br BR/EDR Connection specific Info
*/
struct bt_conn_info { struct bt_conn_info {
/** Connection Type */
uint8_t type; uint8_t type;
/** Connection Role */
uint8_t role; uint8_t role;
union { union __unnamed_workaround__ {
/** LE Connection specific Info */
struct bt_conn_le_info le; struct bt_conn_le_info le;
/** BR/EDR Connection specific Info */
struct bt_conn_br_info br; struct bt_conn_br_info br;
}; };
}; };

View file

@ -821,24 +821,23 @@ typedef uint8_t (*bt_gatt_read_func_t)(struct bt_conn *conn, uint8_t err,
struct bt_gatt_read_params *params, struct bt_gatt_read_params *params,
const void *data, uint16_t length); const void *data, uint16_t length);
/** @brief GATT Read parameters */ /** @brief GATT Read parameters
* @param func Read attribute callback
* @param handle_count If equals to 1 single.handle and single.offset
* are used. If >1 Read Multiple Characteristic
* Values is performed and handles are used.
* @param handle Attribute handle
* @param offset Attribute data offset
* @param handles Handles to read in Read Multiple Characteristic Values
*/
struct bt_gatt_read_params { struct bt_gatt_read_params {
/** Read attribute callback */
bt_gatt_read_func_t func; bt_gatt_read_func_t func;
/** Handles count.
* If equals to 1 single.handle and single.offset are used.
* If >1 Read Multiple Characteristic Values is performed and handles
* are used.
*/
size_t handle_count; size_t handle_count;
union { union __unnamed_workaround__ {
struct { struct __single {
/** Attribute handle */
uint16_t handle; uint16_t handle;
/** Attribute data offset */
uint16_t offset; uint16_t offset;
} single; } single;
/** Handles to read in Read Multiple Characteristic Values */
uint16_t *handles; uint16_t *handles;
}; };
}; };

View file

@ -107,7 +107,7 @@ struct i2c_msg {
union dev_config { union dev_config {
uint32_t raw; uint32_t raw;
struct { struct __bits {
uint32_t use_10_bit_addr : 1; uint32_t use_10_bit_addr : 1;
uint32_t speed : 3; uint32_t speed : 3;
uint32_t is_master_device : 1; uint32_t is_master_device : 1;

View file

@ -63,8 +63,8 @@ enum sensor_value_type {
*/ */
struct sensor_value { struct sensor_value {
enum sensor_value_type type; enum sensor_value_type type;
union { union __unnamed_workaround__ {
struct { struct __unnamed_workaround__ {
int32_t val1; int32_t val1;
int32_t val2; int32_t val2;
}; };

View file

@ -31,4 +31,19 @@
#include <toolchain/other.h> #include <toolchain/other.h>
#endif #endif
/**
* Workaround for documentation parser limitations
*
* Current documentation parsers (sphinx under Doxygen) don't seem to
* understand well unnamed structs / unions. A workaround is to make
* the parser think there is something like a struct/union/enum name
* that is actually something with no effect to the compiler.
*
* Current choice is to give it a 1B alignment. This basically tells
* the compiler to do what is doing now: align it wherever it thinks
* it should, as a 1B alignment "restriction" fits any other alignment
* restriction we might have.
*/
#define __unnamed_workaround__ __attribute__((__aligned__(1)))
#endif /* _TOOLCHAIN_H */ #endif /* _TOOLCHAIN_H */

View file

@ -88,20 +88,21 @@ typedef void (*uart_irq_callback_t)(struct device *port);
*/ */
typedef void (*uart_irq_config_func_t)(struct device *port); typedef void (*uart_irq_config_func_t)(struct device *port);
/** @brief UART device configuration.*/ /**
* @brief UART device configuration.
*
* @param port Base port number
* @param base Memory mapped base address
* @param regs Register address
* @param sys_clk_freq System clock frequency in Hz
*/
struct uart_device_config { struct uart_device_config {
/** union __unnamed_workaround__ {
* Base port number
* or memory mapped base address
* or register address
*/
union {
uint32_t port; uint32_t port;
uint8_t *base; uint8_t *base;
uint32_t regs; uint32_t regs;
}; };
/** System clock frequency in Hz.*/
uint32_t sys_clk_freq; uint32_t sys_clk_freq;
#ifdef CONFIG_PCI #ifdef CONFIG_PCI