New attribute designated_init: mark a struct as requiring designated init
PATCH v3 1/1 New attribute designated_init: mark a struct as requiring designated init
Some structure types provide a set of fields of which most users will only initialize the subset they care about. Users of these types should always use designated initializers, to avoid relying on the specific structure layout. Examples of this type of structure include the many *_operations structures in Linux, which contain a set of function pointers; these structures occasionally gain a new field, lose an obsolete field, or change the function signature for a field.
Add a new attribute designated_init; when used on a struct, it tells Sparse to warn on any positional initialization of a field in that struct.
The new flag -Wdesignated-init controls these warnings. Since these warnings only fire for structures explicitly tagged with the attribute, enable the warning by default.
Includes documentation and test case.
---
v3: Add Wdesignated-init to cgcc's list of sparse warning options. Note that because of this change, this patch depends on my Wsparse-all patch, to avoid conflicts.
v2: Fixed printing of ->ident to not rely on terminating NUL, as noticed by Chris Li.
cgcc | 2 +- evaluate.c | 10 ++ lib.c | 2 + lib.h | 1 + parse.c | 15 +++ sparse.1 | 24 +++++ symbol.h | 3 +- validation/designated-init.c | 195 ++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 validation/designated-init.c
cgcc +1 -1
@@ -88,7 +88,7 @@ exit 0; sub check_only_option { my ($arg) = @_; - return 1 if $arg =~ /^-W(no-?)?(default-bitfield-sign|one-bit-signed-bitfield|cast-truncate|bitwise|typesign|context|undef|ptr-subtraction-blows|cast-to-as|decl|transparent-union|address-space|enum-mismatch|do-while|old-initializer|non-pointer-null|paren-string|return-void|sparse-all)$/; + return 1 if $arg =~ /^-W(no-?)?(default-bitfield-sign|one-bit-signed-bitfield|cast-truncate|bitwise|typesign|context|undef|ptr-subtraction-blows|cast-to-as|decl|transparent-union|address-space|enum-mismatch|do-while|old-initializer|non-pointer-null|paren-string|return-void|designated-init|sparse-all)$/; return 1 if $arg =~ /^-v(no-?)?(entry|dead)$/; return 0; }
evaluate.c +10 -0
@@ -2366,6 +2366,7 @@ static void handle_list_initializer(struct expression *expr,int lclass;if (e->type !=EXPR_INDEX && e->type !=EXPR_IDENTIFIER ) { +struct symbol *struct_sym;if (!top) { top = e; last =first_subobject (ctype, class, &top);
@@ -2378,6 +2379,15 @@ static void handle_list_initializer(struct expression *expr,DELETE_CURRENT_PTR (e);continue ; } + struct_sym = ctype->type ==SYM_NODE ? ctype->ctype.base_type : ctype; +if (Wdesignated_init && struct_sym->designated_init) +warning (e->pos,"%s%.*s%spositional init of field in %s %s, declared with attribute designated_init" , + ctype->ident ?"in initializer for " :"" , + ctype->ident ? ctype->ident->len :0 , + ctype->ident ? ctype->ident->name :"" , + ctype->ident ?": " :"" , +get_type_name (struct_sym->type), +show_ident (struct_sym->ident));if (jumped) {warning (e->pos,"advancing past deep designator" ); jumped =0 ;
lib.c +2 -0
@@ -197,6 +197,7 @@ int Wcast_truncate = 1;int Wcontext =1 ;int Wdecl =1 ;int Wdefault_bitfield_sign =0 ; +int Wdesignated_init =1 ;int Wdo_while =0 ;int Wenum_mismatch =1 ;int Wnon_pointer_null =1 ;
@@ -378,6 +379,7 @@ static const struct warning { { "context", &Wcontext }, { "decl", &Wdecl }, { "default-bitfield-sign", &Wdefault_bitfield_sign }, + { "designated-init", &Wdesignated_init }, { "do-while", &Wdo_while }, { "enum-mismatch", &Wenum_mismatch }, { "non-pointer-null", &Wnon_pointer_null },
lib.h +1 -0
@@ -97,6 +97,7 @@ extern int Wcast_truncate;extern int Wcontext;extern int Wdecl;extern int Wdefault_bitfield_sign; +extern int Wdesignated_init;extern int Wdo_while;extern int Wenum_mismatch;extern int Wnon_pointer_null;
parse.c +15 -0
@@ -64,6 +64,7 @@ typedef struct token *attr_t(struct token *, struct symbol *,static attr_t attribute_packed, attribute_aligned, attribute_modifier, attribute_address_space, attribute_context, + attribute_designated_init, attribute_transparent_union, ignore_attribute, attribute_mode, attribute_force;
@@ -319,6 +320,10 @@ static struct symbol_op context_op = { .attribute = attribute_context, }; +static struct symbol_op designated_init_op = { + .attribute = attribute_designated_init, +}; +static struct symbol_op transparent_union_op = { .attribute = attribute_transparent_union, };
@@ -453,6 +458,7 @@ static struct init_keyword { { "address_space",NS_KEYWORD, .op = &address_space_op }, { "mode", NS_KEYWORD, .op = &mode_op }, { "context", NS_KEYWORD, .op = &context_op }, + { "designated_init", NS_KEYWORD, .op = &designated_init_op }, { "__transparent_union__", NS_KEYWORD, .op = &transparent_union_op }, { "__mode__", NS_KEYWORD, .op = &mode_op },
@@ -1127,6 +1133,15 @@ static struct token *attribute_context(struct token *token, struct symbol *attr,return token; } +static struct token *attribute_designated_init (struct token *token,struct symbol *attr,struct decl_state *ctx) +{ +if (ctx->ctype.base_type && ctx->ctype.base_type->type ==SYM_STRUCT ) + ctx->ctype.base_type->designated_init =1 ; +else +warning (token->pos,"attribute designated_init applied to non-structure type" ); +return token; +} +static struct token *attribute_transparent_union (struct token *token,struct symbol *attr,struct decl_state *ctx) {if (Wtransparent_union)
sparse.1 +24 -0
@@ -138,6 +138,30 @@ explicitly. Sparse does not issue these warnings by default. . .TP +.B \-Wdesignated\-init +Warn about positional initialization of structs marked as requiring designated +initializers. + +Sparse allows an attribute +.BI __attribute__((designated_init)) +which marks a struct as requiring designated initializers. Sparse will warn +about positional initialization of a struct variable or struct literal of a +type that has this attribute. + +Requiring designated initializers for a particular struct type will insulate +code using that struct type from changes to the layout of the type, avoiding +the need to change initializers for that type unless they initialize a removed +or incompatibly changed field. + +Common examples of this type of struct include collections of function pointers +for the implementations of a class of related operations, for which the default +NULL for an unmentioned field in a designated initializer will correctly +indicate the absence of that operation. + +Sparse issues these warnings by default. To turn them off, use +\fB\-Wno\-designated\-init\fR. +. +.TP .B \-Wdo\-while Warn about do-while loops that do not delimit the loop body with braces.
symbol.h +2 -1
@@ -156,7 +156,8 @@ struct symbol { examined:1, expanding:1, evaluated:1, - string:1; + string:1, + designated_init:1;struct expression *array_size;struct ctype ctype;struct symbol_list *arguments;
validation/designated-init.c new file +195 -0
195 lines not on this page · raw message
Signature · 1 line
1.6.4.3