aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-08-12 22:26:28 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-08-17 00:38:46 +0200
commit8b3cbf54ad42c68f5ea2fa5bc2cef5ce9e608571 (patch)
treed415ba8c9847ce463b0f967878d663737f6e9084
parent8b5d92da3b1919f574decb542d93fde82693fe71 (diff)
downloadsparse-8b3cbf54ad42c68f5ea2fa5bc2cef5ce9e608571.tar.gz
union-cast: teach sparse about union casts
A cast to union type is a GCC extension similar to a compound literal just for union, using the syntax of a cast. However, sparse doesn't know about them and treats them like other casts to non-scalars. So, teach sparse about them, convert them to the corresponding compound literal and add a warning flag to enable/disable the associated warning: -W[no-]union-cast. Note: a difference between union casts and compound literals is that the union casts yield rvalues while compound literals are lvalues but this distinction is not yet done in this series. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
-rw-r--r--Documentation/release-notes/v0.6.3.rst5
-rw-r--r--evaluate.c47
-rw-r--r--options.c2
-rw-r--r--options.h1
-rw-r--r--sparse.16
-rw-r--r--validation/eval/union-cast-no.c1
-rw-r--r--validation/eval/union-cast.c1
7 files changed, 60 insertions, 3 deletions
diff --git a/Documentation/release-notes/v0.6.3.rst b/Documentation/release-notes/v0.6.3.rst
new file mode 100644
index 00000000..1aae742e
--- /dev/null
+++ b/Documentation/release-notes/v0.6.3.rst
@@ -0,0 +1,5 @@
+v0.6.3 (2020-xx-xy)
+===================
+
+* Changes in warnings:
+ "warning: cast to union type" [disable with -Wno-union-cast]
diff --git a/evaluate.c b/evaluate.c
index 0563be93..84713725 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -2948,6 +2948,26 @@ static int cast_flags(struct expression *expr, struct expression *old)
return flags;
}
+///
+// check if a type matches one of the members of a union type
+// @utype: the union type
+// @type: to type to check
+// @return: to identifier of the matching type in the union.
+static struct symbol *find_member_type(struct symbol *utype, struct symbol *type)
+{
+ struct symbol *t, *member;
+
+ if (utype->type != SYM_UNION)
+ return NULL;
+
+ FOR_EACH_PTR(utype->symbol_list, member) {
+ classify_type(member, &t);
+ if (type == t)
+ return member;
+ } END_FOR_EACH_PTR(member);
+ return NULL;
+}
+
static struct symbol *evaluate_compound_literal(struct expression *expr, struct expression *source)
{
struct expression *addr = alloc_expression(expr->pos, EXPR_SYMBOL);
@@ -2973,6 +2993,7 @@ static struct symbol *evaluate_cast(struct expression *expr)
struct expression *source = expr->cast_expression;
struct symbol *ctype;
struct symbol *ttype, *stype;
+ struct symbol *member;
int tclass, sclass;
struct ident *tas = NULL, *sas = NULL;
@@ -3022,8 +3043,32 @@ static struct symbol *evaluate_cast(struct expression *expr)
if (expr->type == EXPR_FORCE_CAST)
goto out;
- if (tclass & (TYPE_COMPOUND | TYPE_FN))
+ if (tclass & (TYPE_COMPOUND | TYPE_FN)) {
+ /*
+ * Special case: cast to union type (GCC extension)
+ * The effect is similar to a compound literal except
+ * that the result is a rvalue.
+ */
+ if ((member = find_member_type(ttype, stype))) {
+ struct expression *item, *init;
+
+ if (Wunion_cast)
+ warning(expr->pos, "cast to union type");
+
+ item = alloc_expression(source->pos, EXPR_IDENTIFIER);
+ item->expr_ident = member->ident;
+ item->ident_expression = source;
+
+ init = alloc_expression(source->pos, EXPR_INITIALIZER);
+ add_expression(&init->expr_list, item);
+
+ // FIXME: this should be a rvalue
+ evaluate_compound_literal(expr, init);
+ return ctype;
+ }
+
warning(expr->pos, "cast to non-scalar");
+ }
if (sclass & TYPE_COMPOUND)
warning(expr->pos, "cast from non-scalar");
diff --git a/options.c b/options.c
index f7e81b84..b25f7150 100644
--- a/options.c
+++ b/options.c
@@ -129,6 +129,7 @@ int Wtransparent_union = 0;
int Wtypesign = 0;
int Wundef = 0;
int Wuninitialized = 1;
+int Wunion_cast = 0;
int Wuniversal_initializer = 0;
int Wunknown_attribute = 0;
int Wvla = 1;
@@ -866,6 +867,7 @@ static const struct flag warnings[] = {
{ "typesign", &Wtypesign },
{ "undef", &Wundef },
{ "uninitialized", &Wuninitialized },
+ { "union-cast", &Wunion_cast },
{ "universal-initializer", &Wuniversal_initializer },
{ "unknown-attribute", &Wunknown_attribute },
{ "vla", &Wvla },
diff --git a/options.h b/options.h
index 070c0dd8..40945e28 100644
--- a/options.h
+++ b/options.h
@@ -128,6 +128,7 @@ extern int Wtransparent_union;
extern int Wtypesign;
extern int Wundef;
extern int Wuninitialized;
+extern int Wunion_cast;
extern int Wuniversal_initializer;
extern int Wunknown_attribute;
extern int Wvla;
diff --git a/sparse.1 b/sparse.1
index 60203d5a..fecd6dd9 100644
--- a/sparse.1
+++ b/sparse.1
@@ -451,6 +451,12 @@ The concerned warnings are, for example, those triggered by
Sparse does not issue these warnings by default, processing '{\ 0\ }'
the same as '{\ }'.
.
+.TP
+.B -Wunion-cast
+Warn on casts to union types.
+
+Sparse does not issues these warnings by default.
+.
.SH MISC OPTIONS
.TP
.B \-\-arch=\fIARCH\fR
diff --git a/validation/eval/union-cast-no.c b/validation/eval/union-cast-no.c
index d06b348d..6ba38db8 100644
--- a/validation/eval/union-cast-no.c
+++ b/validation/eval/union-cast-no.c
@@ -16,7 +16,6 @@ static union u bar(long l)
/*
* check-name: union-cast-no
* check-command: sparse -Wno-union-cast $file
- * check-known-to-fail
*
* check-error-start
eval/union-cast-no.c:13:17: warning: cast to non-scalar
diff --git a/validation/eval/union-cast.c b/validation/eval/union-cast.c
index 1d816753..5bee9e0d 100644
--- a/validation/eval/union-cast.c
+++ b/validation/eval/union-cast.c
@@ -16,7 +16,6 @@ static union u bar(long a)
/*
* check-name: union-cast
* check-command: sparse -Wunion-cast $file
- * check-known-to-fail
*
* check-error-start
eval/union-cast.c:8:17: warning: cast to union type