aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-05-21 18:23:04 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2020-05-21 18:23:04 +0200
commit4d96f72782e076f7c4410394364aa77336c09bd2 (patch)
tree6cc86b72f196f6b4465edb74797903f3cf4711b5
parent18351292096b1b90ed7f3bc34459d371aa095588 (diff)
parent384008074a8d8881fefaa4fdda330120385d1259 (diff)
downloadsparse-4d96f72782e076f7c4410394364aa77336c09bd2.tar.gz
Merge branch 'bad-goto'
* warn when jumping into statement expressions * warn when using undefined labels * warn on defined but unused labels It's not allowed to do a goto into an expression statement. For example, it's not well defined what should happen if such an expression is not otherwise reachable and/or can be optimized away. For such situations GCC issues an error, clang doesn't and produce a valid IR but Spare produce an invalid IR with branches to unexisting BBs. The goals of the patches in this series are: *) to detect such gotos at evaluation time; *) issue a sensible error message; *) avoid the linearization of functions with invalid gotos. The implementation principle behind these is to add a new kind of scope (label_scope), one for the usual function scope of labels one for each statement expressions. This new scope, instead of being used as a real scope for the visibility of labels, is used to mark where labels are defined and where they're used. Using this label scope as a real scope controling the visibility of labels was quite appealing and was the initial drive for this implementation but has the problem of inner scope shadowing earlier occurence of labels identically named. This is of course desired for 'normal' symbols but for labels (which are normally visible in the whole function and which may be used before being declared/defined) it has the disadvantage of: *) inhibiting the detecting of misuses once an inner scope is closed *) allowing several distinct labels with the same name in a single function (this can be regarded as a feature but __label__ at block scope should be used for this) *) create diffrences about what is permssble or not between sparse and GCC or clang.
-rw-r--r--evaluate.c36
-rw-r--r--expand.c2
-rw-r--r--expression.c12
-rw-r--r--linearize.c2
-rw-r--r--parse.c73
-rw-r--r--parse.h4
-rw-r--r--scope.c42
-rw-r--r--scope.h10
-rw-r--r--symbol.c13
-rw-r--r--symbol.h7
-rw-r--r--validation/__func__-scope.c8
-rw-r--r--validation/asm-goto-labels.c (renamed from validation/asm-goto-lables.c)0
-rw-r--r--validation/label-asm.c1
-rw-r--r--validation/label-attr.c2
-rw-r--r--validation/label-scope-cgoto.c82
-rw-r--r--validation/label-scope.c5
-rw-r--r--validation/label-scope1.c42
-rw-r--r--validation/label-scope2.c31
-rw-r--r--validation/label-stmt-expr0.c38
-rw-r--r--validation/label-stmt-expr1.c28
-rw-r--r--validation/label-stmt-expr2.c46
-rw-r--r--validation/label-unused.c29
-rw-r--r--validation/linear/goto-invalid.c18
-rw-r--r--validation/linear/goto-stmt-expr-conditional.c27
-rw-r--r--validation/linear/goto-stmt-expr-short-circuit.c31
-rw-r--r--validation/linear/label-scope-cgoto.c10
-rw-r--r--validation/linear/label-stmt-dropped.c (renamed from validation/discarded-label-statement.c)4
-rw-r--r--validation/linear/label-stmt-expr0.c (renamed from validation/label-expr.c)4
-rw-r--r--validation/linear/label-unreachable.c (renamed from validation/linear/unreachable-label0.c)3
-rw-r--r--validation/nested-functions.c43
-rw-r--r--validation/typeof-safe.c27
31 files changed, 617 insertions, 63 deletions
diff --git a/evaluate.c b/evaluate.c
index b7bb1f52..63d75d90 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -3257,6 +3257,21 @@ static struct symbol *evaluate_offsetof(struct expression *expr)
return size_t_ctype;
}
+static void check_label_declaration(struct position pos, struct symbol *label)
+{
+ switch (label->namespace) {
+ case NS_LABEL:
+ if (label->stmt)
+ break;
+ sparse_error(pos, "label '%s' was not declared", show_ident(label->ident));
+ /* fallthrough */
+ case NS_NONE:
+ current_fn->bogus_linear = 1;
+ default:
+ break;
+ }
+}
+
struct symbol *evaluate_expression(struct expression *expr)
{
if (!expr)
@@ -3329,6 +3344,7 @@ struct symbol *evaluate_expression(struct expression *expr)
case EXPR_LABEL:
expr->ctype = &ptr_ctype;
+ check_label_declaration(expr->pos, expr->label_symbol);
return &ptr_ctype;
case EXPR_TYPE:
@@ -3422,7 +3438,7 @@ static struct symbol *evaluate_symbol(struct symbol *sym)
if (sym->definition && sym->definition != sym)
return evaluate_symbol(sym->definition);
- current_fn = base_type;
+ current_fn = sym;
examine_fn_arguments(base_type);
if (!base_type->stmt && base_type->inline_stmt)
@@ -3450,13 +3466,14 @@ void evaluate_symbol_list(struct symbol_list *list)
static struct symbol *evaluate_return_expression(struct statement *stmt)
{
struct expression *expr = stmt->expression;
- struct symbol *fntype;
+ struct symbol *fntype, *rettype;
evaluate_expression(expr);
fntype = current_fn->ctype.base_type;
- if (!fntype || fntype == &void_ctype) {
+ rettype = fntype->ctype.base_type;
+ if (!rettype || rettype == &void_ctype) {
if (expr && expr->ctype != &void_ctype)
- expression_error(expr, "return expression in %s function", fntype?"void":"typeless");
+ expression_error(expr, "return expression in %s function", rettype?"void":"typeless");
if (expr && Wreturn_void)
warning(stmt->pos, "returning void-valued expression");
return NULL;
@@ -3468,7 +3485,7 @@ static struct symbol *evaluate_return_expression(struct statement *stmt)
}
if (!expr->ctype)
return NULL;
- compatible_assignment_types(expr, fntype, &stmt->expression, "return expression");
+ compatible_assignment_types(expr, rettype, &stmt->expression, "return expression");
return NULL;
}
@@ -3741,10 +3758,13 @@ static void evaluate_goto_statement(struct statement *stmt)
{
struct symbol *label = stmt->goto_label;
- if (label && !label->stmt && label->ident && !lookup_keyword(label->ident, NS_KEYWORD))
- sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident));
+ if (!label) {
+ // no label associated, may be a computed goto
+ evaluate_expression(stmt->goto_expression);
+ return;
+ }
- evaluate_expression(stmt->goto_expression);
+ check_label_declaration(stmt->pos, label);
}
struct symbol *evaluate_statement(struct statement *stmt)
diff --git a/expand.c b/expand.c
index e7559878..ab296c73 100644
--- a/expand.c
+++ b/expand.c
@@ -918,7 +918,7 @@ static int expand_symbol_call(struct expression *expr, int cost)
struct symbol *fn = def->ctype.base_type;
struct symbol *curr = current_fn;
- current_fn = fn;
+ current_fn = def;
evaluate_statement(expr->statement);
current_fn = curr;
diff --git a/expression.c b/expression.c
index 5b9bddfe..99a6d756 100644
--- a/expression.c
+++ b/expression.c
@@ -71,9 +71,9 @@ struct token *parens_expression(struct token *token, struct expression **expr, c
struct statement *stmt = alloc_statement(token->pos, STMT_COMPOUND);
*expr = e;
e->statement = stmt;
- start_symbol_scope();
+ start_label_scope();
token = compound_statement(token->next, stmt);
- end_symbol_scope();
+ end_label_scope();
token = expect(token, '}', "at end of statement expression");
} else
token = parse_expression(token, expr);
@@ -124,9 +124,8 @@ static struct symbol *handle_func(struct token *token)
decl->ctype.modifiers = MOD_STATIC;
decl->endpos = token->pos;
- /* function-scope, but in NS_SYMBOL */
- bind_symbol(decl, ident, NS_LABEL);
- decl->namespace = NS_SYMBOL;
+ /* NS_SYMBOL but in function-scope */
+ bind_symbol_with_scope(decl, ident, NS_SYMBOL, function_scope);
len = current_fn->ident->len;
string = __alloc_string(len + 1);
@@ -687,11 +686,12 @@ static struct token *unary_expression(struct token *token, struct expression **t
if (match_op(token, SPECIAL_LOGICAL_AND) &&
token_type(token->next) == TOKEN_IDENT) {
struct expression *label = alloc_expression(token->pos, EXPR_LABEL);
- struct symbol *sym = label_symbol(token->next);
+ struct symbol *sym = label_symbol(token->next, 1);
if (!(sym->ctype.modifiers & MOD_ADDRESSABLE)) {
sym->ctype.modifiers |= MOD_ADDRESSABLE;
add_symbol(&function_computed_target_list, sym);
}
+ check_label_usage(sym, token->pos);
label->flags = CEF_ADDR;
label->label_symbol = sym;
*tree = label;
diff --git a/linearize.c b/linearize.c
index b040d345..49274681 100644
--- a/linearize.c
+++ b/linearize.c
@@ -2480,7 +2480,7 @@ static struct entrypoint *linearize_fn(struct symbol *sym, struct symbol *base_t
pseudo_t result;
int i;
- if (!stmt)
+ if (!stmt || sym->bogus_linear)
return NULL;
ep = alloc_entrypoint();
diff --git a/parse.c b/parse.c
index c1a72ae2..1ec95228 100644
--- a/parse.c
+++ b/parse.c
@@ -721,12 +721,14 @@ static struct symbol * alloc_indirect_symbol(struct position pos, struct ctype *
* it also ends up using function scope instead of the
* regular symbol scope.
*/
-struct symbol *label_symbol(struct token *token)
+struct symbol *label_symbol(struct token *token, int used)
{
struct symbol *sym = lookup_symbol(token->ident, NS_LABEL);
if (!sym) {
sym = alloc_symbol(token->pos, SYM_LABEL);
bind_symbol(sym, token->ident, NS_LABEL);
+ if (used)
+ sym->used = 1;
fn_local_symbol(sym);
}
return sym;
@@ -2127,7 +2129,7 @@ static struct token *parse_asm_labels(struct token *token, struct statement *stm
token = token->next; /* skip ':' and ',' */
if (token_type(token) != TOKEN_IDENT)
return token;
- label = label_symbol(token);
+ label = label_symbol(token, 1);
add_symbol(labels, label);
token = token->next;
} while (match_op(token, ','));
@@ -2218,7 +2220,7 @@ static void start_iterator(struct statement *stmt)
{
struct symbol *cont, *brk;
- start_symbol_scope();
+ start_block_scope();
cont = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(cont, &continue_ident, NS_ITERATOR);
brk = alloc_symbol(stmt->pos, SYM_NODE);
@@ -2233,7 +2235,7 @@ static void start_iterator(struct statement *stmt)
static void end_iterator(struct statement *stmt)
{
- end_symbol_scope();
+ end_block_scope();
}
static struct statement *start_function(struct symbol *sym)
@@ -2278,7 +2280,7 @@ static void start_switch(struct statement *stmt)
{
struct symbol *brk, *switch_case;
- start_symbol_scope();
+ start_block_scope();
brk = alloc_symbol(stmt->pos, SYM_NODE);
bind_symbol(brk, &break_ident, NS_ITERATOR);
@@ -2298,7 +2300,7 @@ static void end_switch(struct statement *stmt)
{
if (!stmt->switch_case->symbol_list)
warning(stmt->pos, "switch with no cases");
- end_symbol_scope();
+ end_block_scope();
}
static void add_case_statement(struct statement *stmt)
@@ -2468,6 +2470,27 @@ static struct token *parse_switch_statement(struct token *token, struct statemen
return token;
}
+static void warn_label_usage(struct position def, struct position use, struct ident *ident)
+{
+ const char *id = show_ident(ident);
+ sparse_error(use, "label '%s' used outside statement expression", id);
+ info(def, " label '%s' defined here", id);
+ current_fn->bogus_linear = 1;
+}
+
+void check_label_usage(struct symbol *label, struct position use_pos)
+{
+ struct statement *def = label->stmt;
+
+ if (def) {
+ if (!is_in_scope(def->label_scope, label_scope))
+ warn_label_usage(def->pos, use_pos, label->ident);
+ } else if (!label->label_scope) {
+ label->label_scope = label_scope;
+ label->label_pos = use_pos;
+ }
+}
+
static struct token *parse_goto_statement(struct token *token, struct statement *stmt)
{
stmt->type = STMT_GOTO;
@@ -2476,7 +2499,9 @@ static struct token *parse_goto_statement(struct token *token, struct statement
token = parse_expression(token->next, &stmt->goto_expression);
add_statement(&function_computed_goto_list, stmt);
} else if (token_type(token) == TOKEN_IDENT) {
- stmt->goto_label = label_symbol(token);
+ struct symbol *label = label_symbol(token, 1);
+ stmt->goto_label = label;
+ check_label_usage(label, stmt->pos);
token = token->next;
} else {
sparse_error(token->pos, "Expected identifier or goto expression");
@@ -2517,6 +2542,15 @@ static struct token *parse_range_statement(struct token *token, struct statement
return expect(token, ';', "after range statement");
}
+static struct token *handle_label_attributes(struct token *token, struct symbol *label)
+{
+ struct decl_state ctx = { };
+
+ token = handle_attributes(token, &ctx, KW_ATTRIBUTE);
+ label->label_modifiers = ctx.ctype.modifiers;
+ return token;
+}
+
static struct token *statement(struct token *token, struct statement **tree)
{
struct statement *stmt = alloc_statement(token->pos, STMT_NONE);
@@ -2528,8 +2562,8 @@ static struct token *statement(struct token *token, struct statement **tree)
return s->op->statement(token, stmt);
if (match_op(token->next, ':')) {
- struct symbol *s = label_symbol(token);
- token = skip_attributes(token->next->next);
+ struct symbol *s = label_symbol(token, 0);
+ token = handle_label_attributes(token->next->next, s);
if (s->stmt) {
sparse_error(stmt->pos, "label '%s' redefined", show_ident(s->ident));
// skip the label to avoid multiple definitions
@@ -2537,17 +2571,18 @@ static struct token *statement(struct token *token, struct statement **tree)
}
stmt->type = STMT_LABEL;
stmt->label_identifier = s;
+ stmt->label_scope = label_scope;
+ if (s->label_scope) {
+ if (!is_in_scope(label_scope, s->label_scope))
+ warn_label_usage(stmt->pos, s->label_pos, s->ident);
+ }
s->stmt = stmt;
return statement(token, &stmt->label_statement);
}
}
if (match_op(token, '{')) {
- stmt->type = STMT_COMPOUND;
- start_symbol_scope();
token = compound_statement(token->next, stmt);
- end_symbol_scope();
-
return expect(token, '}', "at end of compound statement");
}
@@ -2561,8 +2596,7 @@ static struct token *label_statement(struct token *token)
while (token_type(token) == TOKEN_IDENT) {
struct symbol *sym = alloc_symbol(token->pos, SYM_LABEL);
/* it's block-scope, but we want label namespace */
- bind_symbol(sym, token->ident, NS_SYMBOL);
- sym->namespace = NS_LABEL;
+ bind_symbol_with_scope(sym, token->ident, NS_LABEL, block_scope);
fn_local_symbol(sym);
token = token->next;
if (!match_op(token, ','))
@@ -2654,7 +2688,10 @@ static struct token *parameter_type_list(struct token *token, struct symbol *fn)
struct token *compound_statement(struct token *token, struct statement *stmt)
{
+ stmt->type = STMT_COMPOUND;
+ start_block_scope();
token = statement_list(token, &stmt->stmts);
+ end_block_scope();
return token;
}
@@ -2806,15 +2843,15 @@ static struct token *parse_function_body(struct token *token, struct symbol *dec
decl->ctype.modifiers |= MOD_EXTERN;
stmt = start_function(decl);
-
*p = stmt;
+
FOR_EACH_PTR (base_type->arguments, arg) {
declare_argument(arg, base_type);
} END_FOR_EACH_PTR(arg);
- token = compound_statement(token->next, stmt);
-
+ token = statement_list(token->next, &stmt->stmts);
end_function(decl);
+
if (!(decl->ctype.modifiers & MOD_INLINE))
add_symbol(list, decl);
check_declaration(decl);
diff --git a/parse.h b/parse.h
index 0742a2a8..5ac9a23b 100644
--- a/parse.h
+++ b/parse.h
@@ -72,6 +72,7 @@ struct statement {
};
struct /* labeled_struct */ {
struct symbol *label_identifier;
+ struct scope *label_scope;
struct statement *label_statement;
};
struct /* case_struct */ {
@@ -123,7 +124,8 @@ extern struct symbol_list *function_computed_target_list;
extern struct statement_list *function_computed_goto_list;
extern struct token *parse_expression(struct token *, struct expression **);
-extern struct symbol *label_symbol(struct token *token);
+extern struct symbol *label_symbol(struct token *token, int used);
+extern void check_label_usage(struct symbol *label, struct position use_pos);
extern int show_statement(struct statement *);
extern void show_statement_list(struct statement_list *, const char *);
diff --git a/scope.c b/scope.c
index 420c0f5a..fb587c47 100644
--- a/scope.c
+++ b/scope.c
@@ -36,6 +36,7 @@
static struct scope builtin_scope = { .next = &builtin_scope };
struct scope *block_scope = &builtin_scope, // regular automatic variables etc
+ *label_scope = NULL, // expr-stmt labels
*function_scope = &builtin_scope, // labels, arguments etc
*file_scope = &builtin_scope, // static
*global_scope = &builtin_scope; // externally visible
@@ -68,7 +69,6 @@ void rebind_scope(struct symbol *sym, struct scope *new)
static void start_scope(struct scope **s)
{
struct scope *scope = __alloc_scope(0);
- memset(scope, 0, sizeof(*scope));
scope->next = *s;
*s = scope;
}
@@ -77,7 +77,6 @@ void start_file_scope(void)
{
struct scope *scope = __alloc_scope(0);
- memset(scope, 0, sizeof(*scope));
scope->next = &builtin_scope;
file_scope = scope;
@@ -86,15 +85,16 @@ void start_file_scope(void)
block_scope = scope;
}
-void start_symbol_scope(void)
+void start_block_scope(void)
{
start_scope(&block_scope);
}
void start_function_scope(void)
{
- start_scope(&function_scope);
start_scope(&block_scope);
+ start_scope(&label_scope);
+ function_scope = label_scope;
}
static void remove_symbol_scope(struct symbol *sym)
@@ -131,7 +131,7 @@ void new_file_scope(void)
start_file_scope();
}
-void end_symbol_scope(void)
+void end_block_scope(void)
{
end_scope(&block_scope);
}
@@ -139,7 +139,28 @@ void end_symbol_scope(void)
void end_function_scope(void)
{
end_scope(&block_scope);
- end_scope(&function_scope);
+ end_label_scope();
+ function_scope = label_scope;
+}
+
+void start_label_scope(void)
+{
+ start_scope(&label_scope);
+}
+
+void end_label_scope(void)
+{
+ struct symbol *sym;
+
+ FOR_EACH_PTR(label_scope->symbols, sym) {
+ if (!sym->stmt || sym->used)
+ continue;
+ if (sym->label_modifiers & MOD_UNUSED)
+ continue;
+ warning(sym->pos, "unused label '%s'", show_ident(sym->ident));
+ } END_FOR_EACH_PTR(sym);
+
+ end_scope(&label_scope);
}
int is_outer_scope(struct scope *scope)
@@ -151,3 +172,12 @@ int is_outer_scope(struct scope *scope)
return 1;
}
+int is_in_scope(struct scope *outer, struct scope *inner)
+{
+ while (inner != outer) {
+ if (inner == function_scope)
+ return 0;
+ inner = inner->next;
+ }
+ return 1;
+}
diff --git a/scope.h b/scope.h
index 3cad514a..36a56d6a 100644
--- a/scope.h
+++ b/scope.h
@@ -34,6 +34,7 @@ struct scope {
extern struct scope
*block_scope,
+ *label_scope,
*function_scope,
*file_scope,
*global_scope;
@@ -47,15 +48,20 @@ extern void start_file_scope(void);
extern void end_file_scope(void);
extern void new_file_scope(void);
-extern void start_symbol_scope(void);
-extern void end_symbol_scope(void);
+extern void start_block_scope(void);
+extern void end_block_scope(void);
extern void start_function_scope(void);
extern void end_function_scope(void);
+extern void start_label_scope(void);
+extern void end_label_scope(void);
+
extern void set_current_scope(struct symbol *);
extern void bind_scope(struct symbol *, struct scope *);
extern void rebind_scope(struct symbol *, struct scope *);
extern int is_outer_scope(struct scope *);
+extern int is_in_scope(struct scope *outer, struct scope *inner);
+
#endif
diff --git a/symbol.c b/symbol.c
index c2e6f0b4..7044ab3f 100644
--- a/symbol.c
+++ b/symbol.c
@@ -671,9 +671,8 @@ static void inherit_static(struct symbol *sym)
}
}
-void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns)
+void bind_symbol_with_scope(struct symbol *sym, struct ident *ident, enum namespace ns, struct scope *scope)
{
- struct scope *scope;
if (sym->bound) {
sparse_error(sym->pos, "internal error: symbol type already bound");
return;
@@ -690,7 +689,6 @@ void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns)
sym->ident = ident;
sym->bound = 1;
- scope = block_scope;
if (ns == NS_SYMBOL && toplevel(scope)) {
unsigned mod = MOD_ADDRESSABLE | MOD_TOPLEVEL;
@@ -704,11 +702,18 @@ void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns)
}
sym->ctype.modifiers |= mod;
}
+ bind_scope(sym, scope);
+}
+
+void bind_symbol(struct symbol *sym, struct ident *ident, enum namespace ns)
+{
+ struct scope *scope = block_scope;;
+
if (ns == NS_MACRO)
scope = file_scope;
if (ns == NS_LABEL)
scope = function_scope;
- bind_scope(sym, scope);
+ bind_symbol_with_scope(sym, ident, ns, scope);
}
struct symbol *create_symbol(int stream, const char *name, int type, int namespace)
diff --git a/symbol.h b/symbol.h
index 92ccbddb..a16a27c2 100644
--- a/symbol.h
+++ b/symbol.h
@@ -166,10 +166,16 @@ struct symbol {
int (*handler)(struct stream *, struct token **, struct token *);
int normal;
};
+ struct /* NS_LABEL */ {
+ struct scope *label_scope;
+ struct position label_pos;
+ unsigned long label_modifiers;
+ };
struct /* NS_SYMBOL */ {
unsigned long offset;
int bit_size;
unsigned int bit_offset:8,
+ bogus_linear:1,
variadic:1,
initialized:1,
examined:1,
@@ -330,6 +336,7 @@ extern void show_type_list(struct symbol *);
extern void show_symbol_list(struct symbol_list *, const char *);
extern void add_symbol(struct symbol_list **, struct symbol *);
extern void bind_symbol(struct symbol *, struct ident *, enum namespace);
+extern void bind_symbol_with_scope(struct symbol *, struct ident *, enum namespace, struct scope *);
extern struct symbol *examine_symbol_type(struct symbol *);
extern struct symbol *examine_pointer_target(struct symbol *);
diff --git a/validation/__func__-scope.c b/validation/__func__-scope.c
new file mode 100644
index 00000000..508a8b91
--- /dev/null
+++ b/validation/__func__-scope.c
@@ -0,0 +1,8 @@
+static void foo(void)
+{
+ const char *name = ({ __func__; });
+}
+/*
+ * check-name: __func__'s scope
+ * check-command: sparse -Wall $file
+ */
diff --git a/validation/asm-goto-lables.c b/validation/asm-goto-labels.c
index ac2bf2ad..ac2bf2ad 100644
--- a/validation/asm-goto-lables.c
+++ b/validation/asm-goto-labels.c
diff --git a/validation/label-asm.c b/validation/label-asm.c
index 411020ac..b58d1e52 100644
--- a/validation/label-asm.c
+++ b/validation/label-asm.c
@@ -3,6 +3,7 @@
static void f(void)
{
barrier();
+ goto l;
l:
barrier();
}
diff --git a/validation/label-attr.c b/validation/label-attr.c
index a82d7bc9..81c4ac3c 100644
--- a/validation/label-attr.c
+++ b/validation/label-attr.c
@@ -1,6 +1,6 @@
static int foo(void)
{
- return 0;
+ goto rtattr_failure;
rtattr_failure: __attribute__ ((unused))
return -1;
}
diff --git a/validation/label-scope-cgoto.c b/validation/label-scope-cgoto.c
new file mode 100644
index 00000000..1edb9948
--- /dev/null
+++ b/validation/label-scope-cgoto.c
@@ -0,0 +1,82 @@
+void foo(void)
+{
+ void *p = &&l;
+ {
+l: ;
+ }
+ goto *p; // OK
+}
+
+void bar(void)
+{
+ void *p = &&l; // KO: 'jump' inside
+ ({
+l: 1;
+ });
+ goto *p;
+}
+
+void baz(void)
+{
+ void *p = &&l; // KO: 'jump' inside
+ 0 ? 1 : ({
+l: 1;
+ });
+ goto *p;
+}
+
+void qux(void)
+{
+ void *p = &&l; // KO: 'jump' inside + removed
+ 1 ? 1 : ({
+l: 1;
+ });
+ goto *p;
+}
+
+void quz(void)
+{
+ void *p;
+ p = &&l; // KO: undeclared
+ goto *p;
+}
+
+void qxu(void)
+{
+ void *p;
+ ({
+l: 1;
+ });
+ p = &&l; // KO: 'jump' inside
+ goto *p;
+}
+
+void qzu(void)
+{
+ void *p;
+ 1 ? 1 : ({
+l: 1;
+ });
+ p = &&l; // KO: 'jump' inside + removed
+ goto *p;
+}
+
+
+/*
+ * check-name: label-scope-cgoto
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+label-scope-cgoto.c:12:19: error: label 'l' used outside statement expression
+label-scope-cgoto.c:14:1: label 'l' defined here
+label-scope-cgoto.c:21:19: error: label 'l' used outside statement expression
+label-scope-cgoto.c:23:1: label 'l' defined here
+label-scope-cgoto.c:30:19: error: label 'l' used outside statement expression
+label-scope-cgoto.c:32:1: label 'l' defined here
+label-scope-cgoto.c:50:13: error: label 'l' used outside statement expression
+label-scope-cgoto.c:48:1: label 'l' defined here
+label-scope-cgoto.c:60:13: error: label 'l' used outside statement expression
+label-scope-cgoto.c:58:1: label 'l' defined here
+label-scope-cgoto.c:40:13: error: label 'l' was not declared
+ * check-error-end
+ */
diff --git a/validation/label-scope.c b/validation/label-scope.c
index 7af3d916..0ffaaf4a 100644
--- a/validation/label-scope.c
+++ b/validation/label-scope.c
@@ -3,10 +3,7 @@ static int f(int n)
__label__ n;
n: return n;
}
-static int g(int n)
-{
-n: return n;
-}
+
/*
* check-name: __label__ scope
*/
diff --git a/validation/label-scope1.c b/validation/label-scope1.c
new file mode 100644
index 00000000..f2b1ae9b
--- /dev/null
+++ b/validation/label-scope1.c
@@ -0,0 +1,42 @@
+static void ok_top(void)
+{
+ __label__ l;
+l:
+ goto l;
+}
+
+static void ko_undecl(void)
+{
+ __label__ l;
+ goto l; // KO: undeclared
+}
+
+static void ok_local(void)
+{
+l:
+ {
+ __label__ l;
+l:
+ goto l;
+ }
+goto l;
+}
+
+static void ko_scope(void)
+{
+ {
+ __label__ l;
+l:
+ goto l;
+ }
+goto l; // KO: undeclared
+}
+
+/*
+ * check-name: label-scope1
+ *
+ * check-error-start
+label-scope1.c:11:9: error: label 'l' was not declared
+label-scope1.c:32:1: error: label 'l' was not declared
+ * check-error-end
+ */
diff --git a/validation/label-scope2.c b/validation/label-scope2.c
new file mode 100644
index 00000000..44864752
--- /dev/null
+++ b/validation/label-scope2.c
@@ -0,0 +1,31 @@
+static void ok_lvl2(void)
+{
+ __label__ l;
+
+ {
+ l:
+ goto l;
+ }
+}
+
+static void ko_expr2(void)
+{
+ {
+ __label__ a;
+
+ ({
+a:
+ 0;
+ });
+ goto a;
+ }
+}
+
+/*
+ * check-name: label-scope2
+ *
+ * check-error-start
+label-scope2.c:20:17: error: label 'a' used outside statement expression
+label-scope2.c:17:1: label 'a' defined here
+ * check-error-end
+ */
diff --git a/validation/label-stmt-expr0.c b/validation/label-stmt-expr0.c
new file mode 100644
index 00000000..5fc452ab
--- /dev/null
+++ b/validation/label-stmt-expr0.c
@@ -0,0 +1,38 @@
+void aft(void)
+{
+ ({
+l: 1;
+ });
+ goto l; // KO
+}
+
+void bef(void)
+{
+ goto l; // KO
+ ({
+l: 1;
+ });
+}
+
+void lab(void)
+{
+ __label__ l;
+ ({
+l: 1;
+ });
+ goto l; // KO
+}
+
+/*
+ * check-name: label-stmt-expr0
+ * check-command: sparse -Wno-decl $file
+ *
+ * check-error-start
+label-stmt-expr0.c:6:9: error: label 'l' used outside statement expression
+label-stmt-expr0.c:4:1: label 'l' defined here
+label-stmt-expr0.c:11:9: error: label 'l' used outside statement expression
+label-stmt-expr0.c:13:1: label 'l' defined here
+label-stmt-expr0.c:23:9: error: label 'l' used outside statement expression
+label-stmt-expr0.c:21:1: label 'l' defined here
+ * check-error-end
+ */
diff --git a/validation/label-stmt-expr1.c b/validation/label-stmt-expr1.c
new file mode 100644
index 00000000..32a89aad
--- /dev/null
+++ b/validation/label-stmt-expr1.c
@@ -0,0 +1,28 @@
+static int foo(void)
+{
+ goto l;
+ ({
+l:
+ 0;
+ });
+}
+
+static void bar(void)
+{
+ ({
+l:
+ 0;
+ });
+ goto l;
+}
+
+/*
+ * check-name: label-stmt-expr1
+ *
+ * check-error-start
+label-stmt-expr1.c:3:9: error: label 'l' used outside statement expression
+label-stmt-expr1.c:5:1: label 'l' defined here
+label-stmt-expr1.c:16:9: error: label 'l' used outside statement expression
+label-stmt-expr1.c:13:1: label 'l' defined here
+ * check-error-end
+ */
diff --git a/validation/label-stmt-expr2.c b/validation/label-stmt-expr2.c
new file mode 100644
index 00000000..8c54477a
--- /dev/null
+++ b/validation/label-stmt-expr2.c
@@ -0,0 +1,46 @@
+static int foo(void)
+{
+ goto l;
+ ({
+l:
+ 0;
+ });
+ goto l;
+}
+
+static void bar(void)
+{
+ goto l;
+ goto l;
+ ({
+l:
+ 0;
+ });
+}
+
+static void baz(void)
+{
+ ({
+l:
+ 0;
+ });
+ goto l;
+ goto l;
+}
+
+/*
+ * check-name: label-stmt-expr2
+ *
+ * check-error-start
+label-stmt-expr2.c:3:9: error: label 'l' used outside statement expression
+label-stmt-expr2.c:5:1: label 'l' defined here
+label-stmt-expr2.c:8:9: error: label 'l' used outside statement expression
+label-stmt-expr2.c:5:1: label 'l' defined here
+label-stmt-expr2.c:13:9: error: label 'l' used outside statement expression
+label-stmt-expr2.c:16:1: label 'l' defined here
+label-stmt-expr2.c:27:9: error: label 'l' used outside statement expression
+label-stmt-expr2.c:24:1: label 'l' defined here
+label-stmt-expr2.c:28:9: error: label 'l' used outside statement expression
+label-stmt-expr2.c:24:1: label 'l' defined here
+ * check-error-end
+ */
diff --git a/validation/label-unused.c b/validation/label-unused.c
new file mode 100644
index 00000000..e3f255e1
--- /dev/null
+++ b/validation/label-unused.c
@@ -0,0 +1,29 @@
+static void foo(void)
+{
+l:
+ return;
+}
+
+static int bar(void)
+{
+ return ({
+l:
+ ;
+ 0;
+ });
+}
+
+static void baz(void)
+{
+l: __attribute__((unused));
+ return;
+}
+
+/*
+ * check-name: label-unused
+ *
+ * check-error-start
+label-unused.c:3:1: warning: unused label 'l'
+label-unused.c:10:1: warning: unused label 'l'
+ * check-error-end
+ */
diff --git a/validation/linear/goto-invalid.c b/validation/linear/goto-invalid.c
new file mode 100644
index 00000000..860b32a4
--- /dev/null
+++ b/validation/linear/goto-invalid.c
@@ -0,0 +1,18 @@
+static void foo(void)
+{
+ goto return;
+}
+
+void bar(void)
+{
+ goto neverland;
+}
+
+/*
+ * check-name: goto-invalid
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-error-ignore
+ * check-output-ignore
+ * check-output-excludes: END
+ */
diff --git a/validation/linear/goto-stmt-expr-conditional.c b/validation/linear/goto-stmt-expr-conditional.c
new file mode 100644
index 00000000..bbfcb3eb
--- /dev/null
+++ b/validation/linear/goto-stmt-expr-conditional.c
@@ -0,0 +1,27 @@
+int t(void)
+{
+ goto inside;
+ return 1 ? 2 : ({
+inside:
+ return 3;
+ 4;
+ });
+}
+
+void f(int x, int y)
+{
+ 1 ? x : ({
+a:
+ y;
+ });
+ goto a;
+}
+
+/*
+ * check-name: goto-stmt-expr-conditional
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-error-ignore
+ * check-output-ignore
+ * check-output-excludes: END
+ */
diff --git a/validation/linear/goto-stmt-expr-short-circuit.c b/validation/linear/goto-stmt-expr-short-circuit.c
new file mode 100644
index 00000000..a5953e73
--- /dev/null
+++ b/validation/linear/goto-stmt-expr-short-circuit.c
@@ -0,0 +1,31 @@
+int foo(int p)
+{
+ goto inside;
+ if (0 && ({
+inside:
+ return 1;
+ 2;
+ }))
+ return 3;
+ return 4;
+}
+
+int bar(int p)
+{
+ if (0 && ({
+inside:
+ return 1;
+ 2;
+ }))
+ return 3;
+ goto inside;
+}
+
+/*
+ * check-name: goto-stmt-expr-short-circuit
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-error-ignore
+ * check-output-ignore
+ * check-output-excludes: END
+ */
diff --git a/validation/linear/label-scope-cgoto.c b/validation/linear/label-scope-cgoto.c
new file mode 100644
index 00000000..0eba05ae
--- /dev/null
+++ b/validation/linear/label-scope-cgoto.c
@@ -0,0 +1,10 @@
+#include <label-scope-cgoto.c>
+
+/*
+ * check-name: linear/label-scope-cgoto
+ * check-command: test-linearize -Wno-decl -I. $file
+ *
+ * check-error-ignore
+ * check-output-ignore
+ * check-output-excludes: END
+ */
diff --git a/validation/discarded-label-statement.c b/validation/linear/label-stmt-dropped.c
index b4e58ac6..74e0f2e6 100644
--- a/validation/discarded-label-statement.c
+++ b/validation/linear/label-stmt-dropped.c
@@ -11,11 +11,13 @@ start:
r += a;
r += b;
+ if (!r)
+ goto start;
return r;
}
/*
- * check-name: discarded-label-statement
+ * check-name: label-stmt-dropped
* check-command: test-linearize $file
*
* check-output-ignore
diff --git a/validation/label-expr.c b/validation/linear/label-stmt-expr0.c
index e578ed00..ff3c0980 100644
--- a/validation/label-expr.c
+++ b/validation/linear/label-stmt-expr0.c
@@ -3,12 +3,12 @@ int foo(void)
{
int r;
- r = ({ label: 1; });
+ r = ({ goto label; label: 1; });
return r;
}
/*
- * check-name: label-expr
+ * check-name: label-stmt-expr0
* check-command: test-linearize $file
* check-output-ignore
*
diff --git a/validation/linear/unreachable-label0.c b/validation/linear/label-unreachable.c
index 695e5cb0..a44e1211 100644
--- a/validation/linear/unreachable-label0.c
+++ b/validation/linear/label-unreachable.c
@@ -10,9 +10,10 @@ label:
}
/*
- * check-name: unreachable-label0
+ * check-name: label-unreachable
* check-command: test-linearize $file
*
+ * check-error-ignore
* check-output-ignore
* check-output-contains: ret\\.
* check-output-excludes: END
diff --git a/validation/nested-functions.c b/validation/nested-functions.c
new file mode 100644
index 00000000..349edb5a
--- /dev/null
+++ b/validation/nested-functions.c
@@ -0,0 +1,43 @@
+static int test_ok(int a, int b)
+{
+ int nested_ok(int i)
+ {
+ return i * 2;
+ }
+ return nested_ok(b);
+}
+
+static int test_ko(int a, int b)
+{
+ int nested_ko(int i)
+ {
+ return i * 2 + a;
+ }
+ return nested_ko(b);
+}
+
+static int test_inline(int a, int b)
+{
+ inline int nested(int i)
+ {
+ return i * 2;
+ }
+ return nested(b);
+}
+
+static int test_inline_ko(int a, int b)
+{
+ inline int nested(int i)
+ {
+ return i * 2 + a;
+ }
+ return nested(b);
+}
+
+/*
+ * check-name: nested-functions
+ *
+ * check-error-start
+nested-functions.c:32:32: warning: unreplaced symbol 'a'
+ * check-error-end
+ */
diff --git a/validation/typeof-safe.c b/validation/typeof-safe.c
index 614863fb..797c759f 100644
--- a/validation/typeof-safe.c
+++ b/validation/typeof-safe.c
@@ -2,22 +2,35 @@
static void test_safe(void)
{
- int __safe obj, *ptr;
- typeof(obj) var = obj;
- typeof(ptr) ptr2 = ptr;
+ int obj;
+ int __safe *ptr;
+
+ int __safe *ptr2 = ptr;
+ typeof(ptr) ptr3 = ptr;
typeof(*ptr) var2 = obj;
- typeof(*ptr) *ptr3 = ptr;
- typeof(obj) *ptr4 = ptr;
+ int __safe var3 = obj;
+ int *ptr4 = &obj;
+ int *ptr5 = ptr; // KO
+
+ typeof(*ptr) sobj;
+ typeof(&sobj) ptr6 = &obj;
+ typeof(&sobj) ptr7 = ptr; // KO
+
obj = obj;
ptr = ptr;
- ptr = &obj;
obj = *ptr;
+ ptr = (int __safe *) &obj;
}
/*
* check-name: typeof-safe
- * check-known-to-fail
*
* check-error-start
+typeof-safe.c:13:21: warning: incorrect type in initializer (different modifiers)
+typeof-safe.c:13:21: expected int *ptr5
+typeof-safe.c:13:21: got int [safe] *ptr
+typeof-safe.c:17:30: warning: incorrect type in initializer (different modifiers)
+typeof-safe.c:17:30: expected int *ptr7
+typeof-safe.c:17:30: got int [safe] *ptr
* check-error-end
*/