aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2021-01-01 22:21:46 +0100
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2021-01-01 22:21:46 +0100
commitfe455dd6b9fb47743271f342691c4b68532f1260 (patch)
tree44e21912adf82caa03e11214fc682bbe460e1b16
parent10283ac0f48df99e02a5cb16a423972ae42de00d (diff)
parent2cf493d1f63f075e6fe0cccbcb7463b1aa773a74 (diff)
downloadsparse-fe455dd6b9fb47743271f342691c4b68532f1260.tar.gz
Merge branch 'packed'
* packed: add support for __packed struct
-rw-r--r--Documentation/TODO.md3
-rw-r--r--linearize.c13
-rw-r--r--parse.c78
-rw-r--r--symbol.c12
-rw-r--r--symbol.h2
-rw-r--r--validation/enum-type-dubious.c18
-rw-r--r--validation/enum-type-exotic.c28
-rw-r--r--validation/packed-bitfield0.c58
-rw-r--r--validation/packed-bitfield1.c27
-rw-r--r--validation/packed-bitfield2.c15
-rw-r--r--validation/packed-bitfield3.c28
-rw-r--r--validation/packed-bitfield4.c18
-rw-r--r--validation/packed-bitfield5.c20
-rw-r--r--validation/packed-deref0.c23
-rw-r--r--validation/packed-struct.c32
-rw-r--r--validation/parsing/enum-attr.c29
-rw-r--r--validation/type-attribute-align.c19
-rw-r--r--validation/type-attribute-as.c33
-rw-r--r--validation/type-attribute-mod.c21
-rw-r--r--validation/type-attribute-qual.c15
20 files changed, 447 insertions, 45 deletions
diff --git a/Documentation/TODO.md b/Documentation/TODO.md
index 4dc9e63a..3f00bb11 100644
--- a/Documentation/TODO.md
+++ b/Documentation/TODO.md
@@ -4,9 +4,6 @@ TODO
Essential
---------
* SSA is broken by simplify_loads() & branches rewriting/simplification
-* attributes of struct, union & enums are ignored (and maybe others too).
- This requires correct support for __packed which itself needs partial
- and unaligned loads & stores (wip)
* add support for bitwise enums (wip)
Documentation
diff --git a/linearize.c b/linearize.c
index 0250c6bb..e80715ab 100644
--- a/linearize.c
+++ b/linearize.c
@@ -977,8 +977,17 @@ static struct symbol *bitfield_base_type(struct symbol *sym)
if (sym) {
if (sym->type == SYM_NODE)
base = base->ctype.base_type;
- if (base->type == SYM_BITFIELD)
- return base->ctype.base_type;
+ if (base->type == SYM_BITFIELD) {
+ base = base->ctype.base_type;
+ if (sym->packed) {
+ int size = bits_to_bytes(sym->bit_offset + sym->bit_size);
+ sym = __alloc_symbol(0);
+ *sym = *base;
+ sym->bit_size = bytes_to_bits(size);
+ return sym;
+ }
+ return base;
+ }
}
return sym;
}
diff --git a/parse.c b/parse.c
index 0b268570..70be616c 100644
--- a/parse.c
+++ b/parse.c
@@ -669,6 +669,8 @@ struct statement *alloc_statement(struct position pos, int type)
static struct token *struct_declaration_list(struct token *token, struct symbol_list **list);
+static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src);
+
static void apply_modifiers(struct position pos, struct decl_state *ctx)
{
struct symbol *ctype;
@@ -717,10 +719,11 @@ static struct token *struct_union_enum_specifier(enum type type,
struct token *token, struct decl_state *ctx,
struct token *(*parse)(struct token *, struct symbol *))
{
+ struct decl_state attr = { };
struct symbol *sym;
struct position *repos;
- token = handle_attributes(token, ctx);
+ token = handle_attributes(token, &attr);
if (token_type(token) == TOKEN_IDENT) {
sym = lookup_symbol(token->ident, NS_STRUCT);
if (!sym ||
@@ -736,35 +739,36 @@ static struct token *struct_union_enum_specifier(enum type type,
ctx->ctype.base_type = sym;
repos = &token->pos;
token = token->next;
- if (match_op(token, '{')) {
- // The following test is actually wrong for empty
- // structs, but (1) they are not C99, (2) gcc does
- // the same thing, and (3) it's easier.
- if (sym->symbol_list)
- error_die(token->pos, "redefinition of %s", show_typename (sym));
- sym->pos = *repos;
- token = parse(token->next, sym);
- token = expect(token, '}', "at end of struct-union-enum-specifier");
-
- // Mark the structure as needing re-examination
- sym->examined = 0;
- sym->endpos = token->pos;
- }
- return token;
- }
+ if (!match_op(token, '{'))
+ return token;
- // private struct/union/enum type
- if (!match_op(token, '{')) {
+ // The following test is actually wrong for empty
+ // structs, but (1) they are not C99, (2) gcc does
+ // the same thing, and (3) it's easier.
+ if (sym->symbol_list)
+ error_die(token->pos, "redefinition of %s", show_typename (sym));
+ sym->pos = *repos;
+
+ // Mark the structure as needing re-examination
+ sym->examined = 0;
+ } else if (match_op(token, '{')) {
+ // private struct/union/enum type
+ sym = alloc_symbol(token->pos, type);
+ set_current_scope(sym); // used by dissect
+ ctx->ctype.base_type = sym;
+ } else {
sparse_error(token->pos, "expected declaration");
ctx->ctype.base_type = &bad_ctype;
return token;
}
- sym = alloc_symbol(token->pos, type);
- set_current_scope(sym); // used by dissect
token = parse(token->next, sym);
- ctx->ctype.base_type = sym;
- token = expect(token, '}', "at end of specifier");
+ token = expect(token, '}', "at end of specifier");
+ attr.ctype.base_type = sym;
+ token = handle_attributes(token, &attr);
+ apply_ctype(token->pos, &sym->ctype, &attr.ctype);
+ sym->packed = attr.packed;
+
sym->endpos = token->pos;
return token;
@@ -1043,8 +1047,6 @@ static struct token *enum_specifier(struct token *token, struct symbol *sym, str
return ret;
}
-static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype);
-
static struct token *typeof_specifier(struct token *token, struct symbol *sym, struct decl_state *ctx)
{
@@ -1056,7 +1058,7 @@ static struct token *typeof_specifier(struct token *token, struct symbol *sym, s
struct symbol *sym;
token = typename(token->next, &sym, NULL);
ctx->ctype.base_type = sym->ctype.base_type;
- apply_ctype(token->pos, &sym->ctype, &ctx->ctype);
+ apply_ctype(token->pos, &ctx->ctype, &sym->ctype);
} else {
struct symbol *typeof_sym = alloc_symbol(token->pos, SYM_TYPEOF);
token = parse_expression(token->next, &typeof_sym->initializer);
@@ -1088,8 +1090,10 @@ static struct token *ignore_attribute(struct token *token, struct symbol *attr,
static struct token *attribute_packed(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
- if (!ctx->ctype.alignment)
+ if (!ctx->ctype.alignment) {
ctx->ctype.alignment = 1;
+ ctx->packed = 1;
+ }
return token;
}
@@ -1427,24 +1431,24 @@ static struct token *generic_qualifier(struct token *next, struct symbol *sym, s
return next;
}
-static void apply_ctype(struct position pos, struct ctype *thistype, struct ctype *ctype)
+static void apply_ctype(struct position pos, struct ctype *dst, struct ctype *src)
{
- unsigned long mod = thistype->modifiers;
+ unsigned long mod = src->modifiers;
if (mod)
- apply_qualifier(&pos, ctype, mod);
+ apply_qualifier(&pos, dst, mod);
/* Context */
- concat_ptr_list((struct ptr_list *)thistype->contexts,
- (struct ptr_list **)&ctype->contexts);
+ concat_ptr_list((struct ptr_list *)src->contexts,
+ (struct ptr_list **)&dst->contexts);
/* Alignment */
- if (thistype->alignment > ctype->alignment)
- ctype->alignment = thistype->alignment;
+ if (src->alignment > dst->alignment)
+ dst->alignment = src->alignment;
/* Address space */
- if (thistype->as)
- ctype->as = thistype->as;
+ if (src->as)
+ dst->as = src->as;
}
static void specifier_conflict(struct position pos, int what, struct ident *new)
@@ -1529,7 +1533,7 @@ static struct token *declaration_specifiers(struct token *token, struct decl_sta
break;
seen |= Set_S | Set_T;
ctx->ctype.base_type = s->ctype.base_type;
- apply_ctype(token->pos, &s->ctype, &ctx->ctype);
+ apply_ctype(token->pos, &ctx->ctype, &s->ctype);
token = token->next;
continue;
}
diff --git a/symbol.c b/symbol.c
index 1a083fb8..aa02c8c5 100644
--- a/symbol.c
+++ b/symbol.c
@@ -88,6 +88,7 @@ struct struct_union_info {
unsigned long bit_size;
int align_size;
char has_flex_array;
+ bool packed;
struct symbol *flex_array;
};
@@ -120,6 +121,7 @@ static int bitfield_base_size(struct symbol *sym)
static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
{
unsigned long bit_size, align_bit_mask;
+ unsigned long alignment;
int base_size;
bit_size = info->bit_size;
@@ -136,7 +138,8 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
info->flex_array = sym;
}
- align_bit_mask = bytes_to_bits(sym->ctype.alignment) - 1;
+ alignment = info->packed ? 1 : sym->ctype.alignment;
+ align_bit_mask = bytes_to_bits(alignment) - 1;
/*
* Bitfields have some very special rules..
@@ -147,7 +150,7 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
// Zero-width fields just fill up the unit.
int width = base_size ? : (bit_offset ? room : 0);
- if (width > room) {
+ if (width > room && !info->packed) {
bit_size = (bit_size + align_bit_mask) & ~align_bit_mask;
bit_offset = 0;
}
@@ -157,6 +160,8 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
info->bit_size = bit_size + width;
// warning (sym->pos, "bitfield: offset=%d:%d size=:%d", sym->offset, sym->bit_offset, width);
+ if (info->packed && sym->type == SYM_NODE)
+ sym->packed = 1;
return;
}
@@ -173,6 +178,7 @@ static void lay_out_struct(struct symbol *sym, struct struct_union_info *info)
static struct symbol * examine_struct_union_type(struct symbol *sym, int advance)
{
struct struct_union_info info = {
+ .packed = sym->packed,
.max_align = 1,
.bit_size = 0,
.align_size = 1
@@ -191,7 +197,7 @@ static struct symbol * examine_struct_union_type(struct symbol *sym, int advance
sparse_error(info.flex_array->pos, "flexible array member '%s' is not last", show_ident(info.flex_array->ident));
examine_symbol_type(member);
- if (member->ctype.alignment > info.max_align) {
+ if (member->ctype.alignment > info.max_align && !sym->packed) {
// Unnamed bitfields do not affect alignment.
if (member->ident || !is_bitfield_type(member))
info.max_align = member->ctype.alignment;
diff --git a/symbol.h b/symbol.h
index 5c5a7f12..15b21452 100644
--- a/symbol.h
+++ b/symbol.h
@@ -112,6 +112,7 @@ struct decl_state {
unsigned char prefer_abstract;
unsigned char autotype;
unsigned char forced;
+ unsigned char packed;
};
struct pseudo;
@@ -192,6 +193,7 @@ struct symbol {
accessed:1,
builtin:1,
torename:1,
+ packed:1,
transparent_union:1;
int rank:3; // arithmetic's rank
struct expression *array_size;
diff --git a/validation/enum-type-dubious.c b/validation/enum-type-dubious.c
new file mode 100644
index 00000000..f2cb39fc
--- /dev/null
+++ b/validation/enum-type-dubious.c
@@ -0,0 +1,18 @@
+enum foobar {
+ FOO = (void*)0,
+ BAR = (void*)1,
+ BAZ = (int*)0,
+ QUX = (int*)123,
+};
+
+/*
+ * check-name: enum-type-dubious
+ * check-known-to-fail
+ *
+ * check-error-start
+validation/enum-type-dubious.c:2:8: error: enumerator value for 'FOO' is not an integer constant
+validation/enum-type-dubious.c:3:8: error: enumerator value for 'BAR' is not an integer constant
+validation/enum-type-dubious.c:4:8: error: enumerator value for 'BAZ' is not an integer constant
+validation/enum-type-dubious.c:5:8: error: enumerator value for 'QUX' is not an integer constant
+ * check-error-end
+ */
diff --git a/validation/enum-type-exotic.c b/validation/enum-type-exotic.c
new file mode 100644
index 00000000..a17ca0ad
--- /dev/null
+++ b/validation/enum-type-exotic.c
@@ -0,0 +1,28 @@
+enum foobar {
+ C = (unsigned char)0,
+ L = 1L,
+};
+
+unsigned int foo(void);
+unsigned int foo(void)
+{
+#ifdef __CHECKER__
+ _Static_assert([typeof(C)] == [enum foobar], "enum type");
+ _Static_assert([typeof(C)] != [unsigned char], "char type");
+#endif
+
+ typeof(C) v = ~0;
+ return v;
+}
+
+/*
+ * check-name: enum-type-exotic
+ * check-description:
+ * GCC type's for C is 'int' or maybe 'unsigned int'
+ * but certainly not 'unsigned char' like here.
+ * check-command: test-linearize -Wno-decl $file
+ * check-known-to-fail
+ *
+ * check-output-ignore
+ * check-output-excludes: ret\\.32 *\\$255
+ */
diff --git a/validation/packed-bitfield0.c b/validation/packed-bitfield0.c
new file mode 100644
index 00000000..2e209161
--- /dev/null
+++ b/validation/packed-bitfield0.c
@@ -0,0 +1,58 @@
+#define alignof(X) __alignof__(X)
+#define __packed __attribute__((packed))
+
+struct sa {
+ int a:7;
+ int c:10;
+ int b:2;
+} __packed;
+_Static_assert(alignof(struct sa) == 1, "alignof(struct sa)");
+_Static_assert( sizeof(struct sa) == 3, "sizeof(struct sa)");
+
+
+static int get_size(void)
+{
+ return sizeof(struct sa);
+}
+
+static void chk_align(struct sa sa, struct sa *p)
+{
+ _Static_assert(alignof(sa) == 1, "alignof(sa)");
+ _Static_assert(alignof(*p) == 1, "alignof(*p)");
+}
+
+static int fp0(struct sa *sa)
+{
+ return sa->c;
+}
+
+static int fpx(struct sa *sa, int idx)
+{
+ return sa[idx].c;
+}
+
+static int fglobal(void)
+{
+ extern struct sa g;
+ return g.c;
+}
+
+static struct sa l;
+static int flocal(void)
+{
+ return l.c;
+}
+
+
+int main(void)
+{
+ extern void fun(struct sa *);
+ struct sa sa = { 0 };
+
+ fun(&sa);
+ return 0;
+}
+
+/*
+ * check-name: packed-bitfield0
+ */
diff --git a/validation/packed-bitfield1.c b/validation/packed-bitfield1.c
new file mode 100644
index 00000000..b7b575ce
--- /dev/null
+++ b/validation/packed-bitfield1.c
@@ -0,0 +1,27 @@
+#define __packed __attribute__((packed))
+
+struct s {
+ unsigned int f0:1;
+ unsigned int f1:1;
+ unsigned int pad:6;
+} __packed;
+_Static_assert(sizeof(struct s) == 1, "sizeof(struct s)");
+
+extern struct s g;
+
+static int foo(struct s *ptr)
+{
+ int f = 0;
+
+ f += g.f0;
+ f += g.f1;
+
+ f += ptr->f0;
+ f += ptr->f1;
+
+ return f;
+}
+
+/*
+ * check-name: packed-bitfield1
+ */
diff --git a/validation/packed-bitfield2.c b/validation/packed-bitfield2.c
new file mode 100644
index 00000000..244204c2
--- /dev/null
+++ b/validation/packed-bitfield2.c
@@ -0,0 +1,15 @@
+struct bf2 {
+ unsigned p1:2;
+ unsigned i1:32;
+ unsigned p2:2;
+ unsigned s9:9;
+ unsigned s9:9;
+ unsigned s9:9;
+ unsigned b1:1;
+} __attribute__((packed));
+
+_Static_assert(sizeof(struct bf2) == 8);
+
+/*
+ * check-name: packed-bitfield2
+ */
diff --git a/validation/packed-bitfield3.c b/validation/packed-bitfield3.c
new file mode 100644
index 00000000..13368c84
--- /dev/null
+++ b/validation/packed-bitfield3.c
@@ -0,0 +1,28 @@
+#define __packed __attribute__((packed))
+
+typedef unsigned char u8;
+typedef __UINT16_TYPE__ u16;
+typedef __UINT32_TYPE__ u32;
+typedef __UINT64_TYPE__ u64;
+
+struct b {
+ u32 a:1;
+ u32 b:2;
+ u32 c:4;
+ u32 d:8;
+ u32 e:16;
+} __packed;
+_Static_assert(__alignof(struct b) == 1);
+_Static_assert( sizeof(struct b) == 4);
+
+struct c {
+ u8 a;
+ u8 b;
+ u64 c:48;
+} __packed;
+_Static_assert(__alignof(struct c) == 1);
+_Static_assert( sizeof(struct c) == 8);
+
+/*
+ * check-name: packed-bitfield3
+ */
diff --git a/validation/packed-bitfield4.c b/validation/packed-bitfield4.c
new file mode 100644
index 00000000..1a956344
--- /dev/null
+++ b/validation/packed-bitfield4.c
@@ -0,0 +1,18 @@
+#define __packed __attribute__((packed))
+
+typedef __UINT32_TYPE__ u32;
+
+struct s {
+ u32 f:24;
+} __packed;
+_Static_assert(sizeof(struct s) == 3);
+
+static int ld(struct s *s)
+{
+ return s->f;
+}
+
+/*
+ * check-name: packed-bitfield4
+ * check-description: Is check_access() OK with short packed bitfields?
+ */
diff --git a/validation/packed-bitfield5.c b/validation/packed-bitfield5.c
new file mode 100644
index 00000000..87dbf9c2
--- /dev/null
+++ b/validation/packed-bitfield5.c
@@ -0,0 +1,20 @@
+#define __packed __attribute__((packed))
+
+typedef __UINT32_TYPE__ u32;
+
+struct s {
+ u32 a:5;
+ u32 f:30;
+ u32 z:5;
+} __packed;
+_Static_assert(sizeof(struct s) == 5);
+
+static int ld(struct s *s)
+{
+ return s->f;
+}
+
+/*
+ * check-name: packed-bitfield5
+ * check-description: is check_access() OK with 'overlapping' packed bitfields?
+ */
diff --git a/validation/packed-deref0.c b/validation/packed-deref0.c
new file mode 100644
index 00000000..d48ad1ac
--- /dev/null
+++ b/validation/packed-deref0.c
@@ -0,0 +1,23 @@
+#define __packed __attribute__((packed))
+
+typedef struct {
+ __INT8_TYPE__ a;
+ __INT16_TYPE__ b;
+ __INT32_TYPE__ c;
+} __packed obj_t;
+
+_Static_assert(sizeof(obj_t) == 7, "sizeof packed struct");
+
+static void foo(obj_t *ptr, int val)
+{
+ ptr->c = val;
+}
+
+static void bar(obj_t o)
+{
+ foo(&o, 0);
+}
+
+/*
+ * check-name: packed-deref0
+ */
diff --git a/validation/packed-struct.c b/validation/packed-struct.c
new file mode 100644
index 00000000..dad22791
--- /dev/null
+++ b/validation/packed-struct.c
@@ -0,0 +1,32 @@
+#define __packed __attribute__((packed))
+
+typedef unsigned char u8;
+typedef __UINT16_TYPE__ u16;
+typedef __UINT32_TYPE__ u32;
+typedef __UINT64_TYPE__ u64;
+
+struct a {
+ u8 a;
+ u8 b;
+ u16 c;
+} __packed;
+_Static_assert(__alignof(struct a) == 1, "align struct");
+_Static_assert( sizeof(struct a) == 4, " size struct");
+
+struct b {
+ u32 a;
+ u32 b;
+} __packed;
+_Static_assert(__alignof(struct b) == 1, "align struct");
+_Static_assert( sizeof(struct b) == 8, "size struct");
+
+struct c {
+ u16 a;
+ u32 b;
+} __packed;
+_Static_assert(__alignof(struct c) == 1, "align struct");
+_Static_assert( sizeof(struct c) == 6, "size struct");
+
+/*
+ * check-name: packed-struct
+ */
diff --git a/validation/parsing/enum-attr.c b/validation/parsing/enum-attr.c
new file mode 100644
index 00000000..8d851a16
--- /dev/null
+++ b/validation/parsing/enum-attr.c
@@ -0,0 +1,29 @@
+#define __attr __attribute__((deprecated))
+
+enum {
+ old __attr,
+ cur __attr = 42,
+ new,
+};
+
+enum odd {
+ odd = __attr 33,
+};
+
+enum bad {
+ bad = 43 __attr,
+};
+
+/*
+ * check-name: enum-attr
+ *
+ * check-error-start
+parsing/enum-attr.c:10:15: error: typename in expression
+parsing/enum-attr.c:10:15: error: undefined identifier '__attribute__'
+parsing/enum-attr.c:10:15: error: bad constant expression type
+parsing/enum-attr.c:10:22: error: Expected } at end of specifier
+parsing/enum-attr.c:10:22: error: got 33
+parsing/enum-attr.c:14:18: error: Expected } at end of specifier
+parsing/enum-attr.c:14:18: error: got __attribute__
+ * check-error-end
+ */
diff --git a/validation/type-attribute-align.c b/validation/type-attribute-align.c
new file mode 100644
index 00000000..5f3db14a
--- /dev/null
+++ b/validation/type-attribute-align.c
@@ -0,0 +1,19 @@
+#define __aligned(N) __attribute__((aligned(N)))
+#define alignof(X) __alignof__(X)
+
+struct s {
+ short a, b, c;
+} __aligned(2*sizeof(short));
+
+static int fs(void) { return sizeof(struct s); }
+static int fa(void) { return alignof(struct s); }
+
+void main(void)
+{
+ _Static_assert( sizeof(struct s) == 4 * sizeof(short), "size");
+ _Static_assert(alignof(struct s) == 2 * sizeof(short), "alignment");
+}
+
+/*
+ * check-name: type-attribute-align
+ */
diff --git a/validation/type-attribute-as.c b/validation/type-attribute-as.c
new file mode 100644
index 00000000..38f06b37
--- /dev/null
+++ b/validation/type-attribute-as.c
@@ -0,0 +1,33 @@
+#define __as __attribute__((address_space(__as)))
+
+struct s {
+ int i;
+} __as;
+
+
+extern void use0(void *);
+extern void use1(void __as *);
+
+void main(void)
+{
+ struct s s;
+ int i;
+
+ use0(&s); // KO
+ use0(&i); // OK
+ use1(&s); // OK
+ use1(&i); // KO
+}
+
+/*
+ * check-name: type-attribute-as
+ *
+ * check-error-start
+type-attribute-as.c:16:15: warning: incorrect type in argument 1 (different address spaces)
+type-attribute-as.c:16:15: expected void *
+type-attribute-as.c:16:15: got struct s __as *
+type-attribute-as.c:19:15: warning: incorrect type in argument 1 (different address spaces)
+type-attribute-as.c:19:15: expected void __as *
+type-attribute-as.c:19:15: got int *
+ * check-error-end
+ */
diff --git a/validation/type-attribute-mod.c b/validation/type-attribute-mod.c
new file mode 100644
index 00000000..d55011df
--- /dev/null
+++ b/validation/type-attribute-mod.c
@@ -0,0 +1,21 @@
+#define __noderef __attribute__((noderef))
+
+struct s {
+ int i;
+} __noderef;
+
+
+void main(void)
+{
+ struct s s;
+
+ s.i = 0;
+}
+
+/*
+ * check-name: type-attribute-mod
+ *
+ * check-error-start
+type-attribute-mod.c:12:9: warning: dereference of noderef expression
+ * check-error-end
+ */
diff --git a/validation/type-attribute-qual.c b/validation/type-attribute-qual.c
new file mode 100644
index 00000000..62d8b98e
--- /dev/null
+++ b/validation/type-attribute-qual.c
@@ -0,0 +1,15 @@
+static const struct s {
+ int x;
+} map[2];
+
+static void foo(struct s *p, int v)
+{
+ p->x += v;
+}
+
+/*
+ * check-name: type-attribute-qual
+ * check-description: When declaring a type and a variable in the same
+ * declaration, ensure that type qualifiers apply to the variable
+ * and not to the type.
+ */