The clang static analyzer can help prevent bugs resulting from allowing NULL to be passed into places it shouldn't be, by allowing pointers in APIs and internals to be annotated with nullability.
This can also help clarify the use of APIs, e.g. whether srunner_create() is allowed to take a NULL Suite as a parameter. (It currently is, but that required source inspection to determine.)
The typical way to use these annotations would be to put them behind macros such as CK_NULLABLE and CK_NONNULL so that with non-clang compilers (or versions of clang too old to have the feature) the macros are just empty. And if other compilers (or the language standard) get nullability annotations, they can be opted in at that point.
As an example, the full declaration of srunner_create() would look like this (also assuming CK_EXPORT and CK_DLL_EXP are combined per #371):
CK_EXPORT SRunner * CK_NULLABLE srunner_create(Suite * CK_NULLABLE s);
In 10+ years of writing C and Objective-C with these types of nullability annotation, I haven't found them to be particularly cumbersome, while I have found them to provide substantial value. They obey regular C rules like const so they apply to the pointer to their left, and since you generally only write them in function signatures they don't overwhelm the code. And the static analysis they enable can help reveal subtle bugs.
One additional trick is to have "assume-nonnull" begin & end macros (e.g. CK_ASSUME_NONNULL_BEGIN and CK_ASSUME_NONNULL_END) that are put around header and source files such that any pointers in function signatures within the macros are assumed to be non-null unless specifically annotated. This further reduces the amount of "noise" in APIs since only places where a NULL is specifically allowed need to be annotated.
The clang static analyzer can help prevent bugs resulting from allowing
NULLto be passed into places it shouldn't be, by allowing pointers in APIs and internals to be annotated with nullability.This can also help clarify the use of APIs, e.g. whether
srunner_create()is allowed to take aNULLSuite as a parameter. (It currently is, but that required source inspection to determine.)The typical way to use these annotations would be to put them behind macros such as
CK_NULLABLEandCK_NONNULLso that with non-clang compilers (or versions of clang too old to have the feature) the macros are just empty. And if other compilers (or the language standard) get nullability annotations, they can be opted in at that point.As an example, the full declaration of
srunner_create()would look like this (also assumingCK_EXPORTandCK_DLL_EXPare combined per #371):In 10+ years of writing C and Objective-C with these types of nullability annotation, I haven't found them to be particularly cumbersome, while I have found them to provide substantial value. They obey regular C rules like
constso they apply to the pointer to their left, and since you generally only write them in function signatures they don't overwhelm the code. And the static analysis they enable can help reveal subtle bugs.One additional trick is to have "assume-nonnull" begin & end macros (e.g.
CK_ASSUME_NONNULL_BEGINandCK_ASSUME_NONNULL_END) that are put around header and source files such that any pointers in function signatures within the macros are assumed to be non-null unless specifically annotated. This further reduces the amount of "noise" in APIs since only places where aNULLis specifically allowed need to be annotated.