Skip to contentSkip to conversation

New attribute designated_init: mark a struct as requiring designated init

PATCH New Josh Triplett 1 participant 1 message Oct 11, 2009 Sparse checker

lore
Share & tools
lore
#0 lore raw

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
index 8005c3c..d37cf88 100755
@@ -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
index 805ae90..28bfd7c 100644
@@ -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
index 963be08..9777826 100644
@@ -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
index 25abb80..3ced1bf 100644
@@ -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
index 5e75242..03fa5d9 100644
@@ -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
index abc75a2..bde6b6d 100644
@@ -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
index 60fdad0..83b3d4b 100644
@@ -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