aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--Documentation/TODO.md12
-rw-r--r--Documentation/nocast-vs-bitwise.md34
-rwxr-xr-xDocumentation/sphinx/cdoc.py10
-rw-r--r--evaluate.c36
-rw-r--r--expand.c2
-rw-r--r--expression.c12
-rw-r--r--linearize.c2
-rw-r--r--machine.h2
-rw-r--r--parse.c131
-rw-r--r--parse.h4
-rw-r--r--scope.c42
-rw-r--r--scope.h10
-rw-r--r--show-parse.c28
-rw-r--r--symbol.c13
-rw-r--r--symbol.h21
-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/attr-visible.c1
-rw-r--r--validation/attr-visible2.c1
-rw-r--r--validation/cond_expr2.c2
-rw-r--r--validation/function-redecl.c6
-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
40 files changed, 701 insertions, 147 deletions
diff --git a/Documentation/TODO.md b/Documentation/TODO.md
index cbda1c39..4344a1dc 100644
--- a/Documentation/TODO.md
+++ b/Documentation/TODO.md
@@ -24,15 +24,9 @@ Core
- add the sym into a list and
- recalculate the addressability before memops's SSA conversion
* bool_ctype should be split into internal 1-bit / external 8-bit
-* Previous declarations and the definition need to be merged. For example,
- in the code here below, the function definition is **not** static:
- ```
- static void foo(void);
- void foo(void) { ... }
- ```
Testsuite
---------
+---------
* there are more than 50 failing tests. They should be fixed
(but most are non-trivial to fix).
@@ -84,9 +78,13 @@ Longer term/to investigate
* should support "-Werror=..." ?
* All warning messages should include the option how to disable it.
For example:
+
"warning: Variable length array is used."
+
should be something like:
+
"warning: Variable length array is used. (-Wno-vla)"
+
* ptrlists must have elements be removed while being iterated but this
is hard to insure it is not done.
* having 'struct symbol' used to represent symbols *and* types is
diff --git a/Documentation/nocast-vs-bitwise.md b/Documentation/nocast-vs-bitwise.md
index b649abcd..9ba5a789 100644
--- a/Documentation/nocast-vs-bitwise.md
+++ b/Documentation/nocast-vs-bitwise.md
@@ -1,4 +1,5 @@
-# __nocast vs __bitwise
+__nocast vs __bitwise
+=====================
`__nocast` warns about explicit or implicit casting to different types.
HOWEVER, it doesn't consider two 32-bit integers to be different
@@ -16,25 +17,26 @@ harder to lose the type by mistake.
So the basic rule is:
- - `__nocast` on its own tends to be more useful for *big* integers
-that still need to act like integers, but you want to make it much
-less likely that they get truncated by mistake. So a 64-bit integer
-that you don't want to mistakenly/silently be returned as `int`, for
-example. But they mix well with random integer types, so you can add
-to them etc without using anything special. However, that mixing also
-means that the `__nocast` really gets lost fairly easily.
-
- - `__bitwise` is for *unique types* that cannot be mixed with other
-types, and that you'd never want to just use as a random integer (the
-integer `0` is special, though, and gets silently accepted - it's
-kind of like `NULL` for pointers). So `gfp_t` or the `safe endianness`
-types would be `__bitwise`: you can only operate on them by doing
-specific operations that know about *that* particular type.
+- `__nocast` on its own tends to be more useful for *big* integers
+ that still need to act like integers, but you want to make it much
+ less likely that they get truncated by mistake. So a 64-bit integer
+ that you don't want to mistakenly/silently be returned as `int`, for
+ example. But they mix well with random integer types, so you can add
+ to them etc without using anything special. However, that mixing also
+ means that the `__nocast` really gets lost fairly easily.
+
+- `__bitwise` is for *unique types* that cannot be mixed with other
+ types, and that you'd never want to just use as a random integer (the
+ integer `0` is special, though, and gets silently accepted - it's
+ kind of like `NULL` for pointers). So `gfp_t` or the `safe endianness`
+ types would be `__bitwise`: you can only operate on them by doing
+ specific operations that know about *that* particular type.
Generally, you want `__bitwise` if you are looking for type safety.
`__nocast` really is pretty weak.
-## Reference:
+Reference:
+----------
* Linus' e-mail about `__nocast` vs `__bitwise`:
diff --git a/Documentation/sphinx/cdoc.py b/Documentation/sphinx/cdoc.py
index 318e9b23..73c128cb 100755
--- a/Documentation/sphinx/cdoc.py
+++ b/Documentation/sphinx/cdoc.py
@@ -269,7 +269,7 @@ if __name__ == '__main__':
dump_doc(extract(sys.stdin, '<stdin>'))
-from sphinx.ext.autodoc import AutodocReporter
+from sphinx.util.docutils import switch_source_input
import docutils
import os
class CDocDirective(docutils.parsers.rst.Directive):
@@ -294,13 +294,13 @@ class CDocDirective(docutils.parsers.rst.Directive):
## let parse this new reST content
memo = self.state.memo
- save = memo.reporter, memo.title_styles, memo.section_level
- memo.reporter = AutodocReporter(lst, memo.reporter)
+ save = memo.title_styles, memo.section_level
node = docutils.nodes.section()
try:
- self.state.nested_parse(lst, 0, node, match_titles=1)
+ with switch_source_input(self.state, lst):
+ self.state.nested_parse(lst, 0, node, match_titles=1)
finally:
- memo.reporter, memo.title_styles, memo.section_level = save
+ memo.title_styles, memo.section_level = save
return node.children
def setup(app):
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/machine.h b/machine.h
index c55e3f1b..9c17dd6a 100644
--- a/machine.h
+++ b/machine.h
@@ -113,7 +113,7 @@ enum {
#elif defined(__sun__) || defined(__sun)
#define OS_NATIVE OS_SUNOS
#elif defined(__unix__) || defined(__unix)
-#define OS_UNIX OS_UNIX
+#define OS_NATIVE OS_UNIX
#else
#define OS_NATIVE OS_UNKNOWN
#endif
diff --git a/parse.c b/parse.c
index 48494afc..687c8c0c 100644
--- a/parse.c
+++ b/parse.c
@@ -84,7 +84,6 @@ typedef struct token *attr_t(struct token *, struct symbol *,
static attr_t
attribute_packed, attribute_aligned, attribute_modifier,
attribute_function,
- attribute_ext_visible,
attribute_bitwise,
attribute_address_space, attribute_context,
attribute_designated_init,
@@ -389,10 +388,6 @@ static struct symbol_op attr_fun_op = {
.attribute = attribute_function,
};
-static struct symbol_op ext_visible_op = {
- .attribute = attribute_ext_visible,
-};
-
static struct symbol_op attr_bitwise_op = {
.attribute = attribute_bitwise,
};
@@ -571,6 +566,10 @@ static struct init_keyword {
{ "__noderef__",NS_KEYWORD, MOD_NODEREF, .op = &attr_mod_op },
{ "safe", NS_KEYWORD, MOD_SAFE, .op = &attr_mod_op },
{ "__safe__", NS_KEYWORD, MOD_SAFE, .op = &attr_mod_op },
+ { "unused", NS_KEYWORD, MOD_UNUSED, .op = &attr_mod_op },
+ { "__unused__", NS_KEYWORD, MOD_UNUSED, .op = &attr_mod_op },
+ { "externally_visible", NS_KEYWORD, MOD_EXT_VISIBLE,.op = &attr_mod_op },
+ { "__externally_visible__", NS_KEYWORD,MOD_EXT_VISIBLE,.op = &attr_mod_op },
{ "force", NS_KEYWORD, .op = &attr_force_op },
{ "__force__", NS_KEYWORD, .op = &attr_force_op },
{ "bitwise", NS_KEYWORD, MOD_BITWISE, .op = &attr_bitwise_op },
@@ -588,8 +587,8 @@ static struct init_keyword {
{"const", NS_KEYWORD, MOD_PURE, .op = &attr_fun_op },
{"__const", NS_KEYWORD, MOD_PURE, .op = &attr_fun_op },
{"__const__", NS_KEYWORD, MOD_PURE, .op = &attr_fun_op },
- {"externally_visible", NS_KEYWORD, .op = &ext_visible_op },
- {"__externally_visible__", NS_KEYWORD, .op = &ext_visible_op },
+ {"gnu_inline", NS_KEYWORD, MOD_GNU_INLINE, .op = &attr_fun_op },
+ {"__gnu_inline__",NS_KEYWORD, MOD_GNU_INLINE, .op = &attr_fun_op },
{ "mode", NS_KEYWORD, .op = &mode_op },
{ "__mode__", NS_KEYWORD, .op = &mode_op },
@@ -722,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;
@@ -1127,11 +1128,16 @@ static struct token *attribute_aligned(struct token *token, struct symbol *attr,
return token;
}
+static void apply_mod(struct position *pos, unsigned long *mods, unsigned long mod)
+{
+ if (*mods & mod & ~MOD_DUP_OK)
+ warning(*pos, "duplicate %s", modifier_string(mod));
+ *mods |= mod;
+}
+
static void apply_qualifier(struct position *pos, struct ctype *ctx, unsigned long qual)
{
- if (ctx->modifiers & qual)
- warning(*pos, "duplicate %s", modifier_string(qual));
- ctx->modifiers |= qual;
+ apply_mod(pos, &ctx->modifiers, qual);
}
static struct token *attribute_modifier(struct token *token, struct symbol *attr, struct decl_state *ctx)
@@ -1142,16 +1148,7 @@ static struct token *attribute_modifier(struct token *token, struct symbol *attr
static struct token *attribute_function(struct token *token, struct symbol *attr, struct decl_state *ctx)
{
- unsigned long mod = attr->ctype.modifiers;
- if (ctx->f_modifiers & mod)
- warning(token->pos, "duplicate %s", modifier_string(mod));
- ctx->f_modifiers |= mod;
- return token;
-}
-
-static struct token *attribute_ext_visible(struct token *token, struct symbol *attr, struct decl_state *ctx)
-{
- ctx->is_ext_visible = 1;
+ apply_mod(&token->pos, &ctx->f_modifiers, attr->ctype.modifiers);
return token;
}
@@ -1382,7 +1379,7 @@ static const char *storage_class[] =
[SForced] = "[force]"
};
-static unsigned long storage_modifiers(struct decl_state *ctx)
+static unsigned long decl_modifiers(struct decl_state *ctx)
{
static unsigned long mod[SMax] =
{
@@ -1391,15 +1388,16 @@ static unsigned long storage_modifiers(struct decl_state *ctx)
[SStatic] = MOD_STATIC,
[SRegister] = MOD_REGISTER
};
- return mod[ctx->storage_class] | (ctx->is_inline ? MOD_INLINE : 0)
- | (ctx->is_tls ? MOD_TLS : 0)
- | (ctx->is_ext_visible ? MOD_EXT_VISIBLE : 0);
+ unsigned long mods = ctx->ctype.modifiers & MOD_DECLARE;
+ ctx->ctype.modifiers &= ~MOD_DECLARE;
+ return mod[ctx->storage_class] | mods;
}
static void set_storage_class(struct position *pos, struct decl_state *ctx, int class)
{
+ int is_tls = ctx->ctype.modifiers & MOD_TLS;
/* __thread can be used alone, or with extern or static */
- if (ctx->is_tls && (class != SStatic && class != SExtern)) {
+ if (is_tls && (class != SStatic && class != SExtern)) {
sparse_error(*pos, "__thread can only be used alone, or with "
"extern or static");
return;
@@ -1450,7 +1448,7 @@ static struct token *thread_specifier(struct token *next, struct decl_state *ctx
/* This GCC extension can be used alone, or with extern or static */
if (!ctx->storage_class || ctx->storage_class == SStatic
|| ctx->storage_class == SExtern) {
- ctx->is_tls = 1;
+ apply_qualifier(&next->pos, &ctx->ctype, MOD_TLS);
} else {
sparse_error(next->pos, "__thread can only be used alone, or "
"with extern or static");
@@ -1467,7 +1465,7 @@ static struct token *attribute_force(struct token *token, struct symbol *attr, s
static struct token *inline_specifier(struct token *next, struct decl_state *ctx)
{
- ctx->is_inline = 1;
+ apply_qualifier(&next->pos, &ctx->ctype, MOD_INLINE);
return next;
}
@@ -1670,7 +1668,7 @@ static struct token *declaration_specifiers(struct token *token, struct decl_sta
}
}
token = token->next;
- if (s->op->declarator)
+ if (s->op->declarator) // Note: this eats attributes
token = s->op->declarator(token, ctx);
if (s->op->type & KW_EXACT) {
ctx->ctype.base_type = s->ctype.base_type;
@@ -1997,7 +1995,7 @@ static struct token *declaration_list(struct token *token, struct symbol_list **
unsigned long mod;
token = declaration_specifiers(token, &ctx);
- mod = storage_modifiers(&ctx);
+ mod = decl_modifiers(&ctx);
saved = ctx.ctype;
for (;;) {
struct symbol *decl = alloc_symbol(token->pos, SYM_NODE);
@@ -2050,7 +2048,7 @@ static struct token *parameter_declaration(struct token *token, struct symbol *s
token = handle_attributes(token, &ctx, KW_ATTRIBUTE);
apply_modifiers(token->pos, &ctx);
sym->ctype = ctx.ctype;
- sym->ctype.modifiers |= storage_modifiers(&ctx);
+ sym->ctype.modifiers |= decl_modifiers(&ctx);
sym->endpos = token->pos;
sym->forced_arg = ctx.storage_class == SForced;
return token;
@@ -2131,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, ','));
@@ -2222,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);
@@ -2237,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)
@@ -2282,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);
@@ -2302,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)
@@ -2472,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;
@@ -2480,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");
@@ -2521,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);
@@ -2532,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
@@ -2541,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");
}
@@ -2565,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, ','))
@@ -2658,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;
}
@@ -2817,15 +2850,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);
@@ -2960,7 +2993,7 @@ struct token *external_declaration(struct token *token, struct symbol_list **lis
/* Parse declaration-specifiers, if any */
token = declaration_specifiers(token, &ctx);
- mod = storage_modifiers(&ctx);
+ mod = decl_modifiers(&ctx);
decl = alloc_symbol(token->pos, SYM_NODE);
/* Just a type declaration? */
if (match_op(token, ';')) {
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/show-parse.c b/show-parse.c
index 8a145b88..eb71b650 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -119,28 +119,30 @@ const char *modifier_string(unsigned long mod)
static struct mod_name mod_names[] = {
{MOD_AUTO, "auto"},
+ {MOD_EXTERN, "extern"},
{MOD_REGISTER, "register"},
{MOD_STATIC, "static"},
- {MOD_EXTERN, "extern"},
+ {MOD_INLINE, "inline"},
{MOD_CONST, "const"},
- {MOD_VOLATILE, "volatile"},
{MOD_RESTRICT, "restrict"},
- {MOD_ATOMIC, "[atomic]"},
- {MOD_SIGNED, "[signed]"},
- {MOD_UNSIGNED, "[unsigned]"},
- {MOD_TLS, "[tls]"},
- {MOD_INLINE, "inline"},
+ {MOD_VOLATILE, "volatile"},
{MOD_ADDRESSABLE, "[addressable]"},
+ {MOD_ASSIGNED, "[assigned]"},
+ {MOD_ATOMIC, "[atomic]"},
+ {MOD_BITWISE, "[bitwise]"},
+ {MOD_EXPLICITLY_SIGNED, "[explicitly-signed]"},
+ {MOD_GNU_INLINE, "[gnu_inline]"},
{MOD_NOCAST, "[nocast]"},
{MOD_NODEREF, "[noderef]"},
- {MOD_TOPLEVEL, "[toplevel]"},
- {MOD_ASSIGNED, "[assigned]"},
- {MOD_SAFE, "[safe]"},
- {MOD_USERTYPE, "[usertype]"},
{MOD_NORETURN, "[noreturn]"},
- {MOD_EXPLICITLY_SIGNED, "[explicitly-signed]"},
- {MOD_BITWISE, "[bitwise]"},
{MOD_PURE, "[pure]"},
+ {MOD_SAFE, "[safe]"},
+ {MOD_SIGNED, "[signed]"},
+ {MOD_TLS, "[tls]"},
+ {MOD_TOPLEVEL, "[toplevel]"},
+ {MOD_UNSIGNED, "[unsigned]"},
+ {MOD_UNUSED, "[unused]"},
+ {MOD_USERTYPE, "[usertype]"},
};
for (i = 0; i < ARRAY_SIZE(mod_names); i++) {
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 18476582..a16a27c2 100644
--- a/symbol.h
+++ b/symbol.h
@@ -108,8 +108,7 @@ struct decl_state {
struct ident **ident;
struct symbol_op *mode;
unsigned long f_modifiers; // function attributes
- unsigned char prefer_abstract, is_inline, storage_class, is_tls;
- unsigned char is_ext_visible;
+ unsigned char prefer_abstract, storage_class;
unsigned char autotype;
};
@@ -167,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,
@@ -229,14 +234,14 @@ struct symbol {
#define MOD_UNSIGNED 0x00004000
#define MOD_EXPLICITLY_SIGNED 0x00008000
- // MOD UNUSED 0x00010000
+#define MOD_GNU_INLINE 0x00010000
#define MOD_USERTYPE 0x00020000
// MOD UNUSED 0x00040000
// MOD UNUSED 0x00080000
// MOD UNUSED 0x00100000
// MOD UNUSED 0x00200000
- // MOD UNUSED 0x00400000
+#define MOD_UNUSED 0x00400000
#define MOD_SAFE 0x00800000 // non-null/non-trapping pointer
#define MOD_PURE 0x01000000
#define MOD_BITWISE 0x02000000
@@ -252,7 +257,7 @@ struct symbol {
#define MOD_ESIGNED (MOD_SIGNED | MOD_EXPLICITLY_SIGNED)
#define MOD_SIGNEDNESS (MOD_SIGNED | MOD_UNSIGNED | MOD_EXPLICITLY_SIGNED)
#define MOD_SPECIFIER MOD_SIGNEDNESS
-#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE)
+#define MOD_IGNORE (MOD_STORAGE | MOD_ACCESS | MOD_USERTYPE | MOD_EXPLICITLY_SIGNED | MOD_EXT_VISIBLE | MOD_UNUSED | MOD_GNU_INLINE)
#define MOD_QUALIFIER (MOD_CONST | MOD_VOLATILE | MOD_RESTRICT)
#define MOD_PTRINHERIT (MOD_QUALIFIER | MOD_ATOMIC | MOD_NODEREF | MOD_NORETURN | MOD_NOCAST)
/* modifiers preserved by typeof() operator */
@@ -261,6 +266,11 @@ struct symbol {
#define MOD_FUN_ATTR (MOD_PURE|MOD_NORETURN)
/* like cvr-qualifiers but 'reversed' (OK: source <= target) */
#define MOD_REV_QUAL (MOD_PURE|MOD_NORETURN)
+/* do not warn when these are duplicated */
+#define MOD_DUP_OK (MOD_UNUSED|MOD_GNU_INLINE)
+/* must be part of the declared symbol, not its type */
+#define MOD_DECLARE (MOD_STORAGE|MOD_INLINE|MOD_TLS|MOD_GNU_INLINE|MOD_UNUSED|MOD_PURE|MOD_NORETURN|MOD_EXT_VISIBLE)
+
/* Current parsing/evaluation function */
@@ -326,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/attr-visible.c b/validation/attr-visible.c
index 38ee8575..ce35e4e5 100644
--- a/validation/attr-visible.c
+++ b/validation/attr-visible.c
@@ -9,5 +9,4 @@ int flag __visible;
/*
* check-name: attr-visible
* check-command: sparse -Wdecl $file
- * check-known-to-fail
*/
diff --git a/validation/attr-visible2.c b/validation/attr-visible2.c
index 62949b47..98918169 100644
--- a/validation/attr-visible2.c
+++ b/validation/attr-visible2.c
@@ -6,5 +6,4 @@ int arr[2] __visible;
/*
* check-name: attr-visible-after
* check-command: sparse -Wdecl $file
- * check-known-to-fail
*/
diff --git a/validation/cond_expr2.c b/validation/cond_expr2.c
index 5e974cfa..a5d96576 100644
--- a/validation/cond_expr2.c
+++ b/validation/cond_expr2.c
@@ -16,7 +16,7 @@ cond_expr2.c:6:11: warning: incorrect type in assignment (different modifiers)
cond_expr2.c:6:11: expected void volatile *extern [addressable] [toplevel] q
cond_expr2.c:6:11: got void const volatile *
cond_expr2.c:8:11: warning: incorrect type in assignment (different modifiers)
-cond_expr2.c:8:11: expected int volatile *extern [addressable] [toplevel] [assigned] r
+cond_expr2.c:8:11: expected int volatile *extern [addressable] [assigned] [toplevel] r
cond_expr2.c:8:11: got int const volatile *
* check-error-end
*/
diff --git a/validation/function-redecl.c b/validation/function-redecl.c
index 03151359..d0d844a5 100644
--- a/validation/function-redecl.c
+++ b/validation/function-redecl.c
@@ -51,11 +51,11 @@ void arg_vararg(int a, ...) { } /* check-should-fail */
function-redecl.c:5:6: error: symbol 'ret_type' redeclared with different type (different base types):
function-redecl.c:5:6: void extern [addressable] [toplevel] ret_type( ... )
function-redecl.c:4:5: note: previously declared as:
-function-redecl.c:4:5: int extern [signed] [addressable] [toplevel] ret_type( ... )
+function-redecl.c:4:5: int extern [addressable] [signed] [toplevel] ret_type( ... )
function-redecl.c:9:11: error: symbol 'ret_const' redeclared with different type (different modifiers):
-function-redecl.c:9:11: int extern const [signed] [addressable] [toplevel] ret_const( ... )
+function-redecl.c:9:11: int extern const [addressable] [signed] [toplevel] ret_const( ... )
function-redecl.c:8:5: note: previously declared as:
-function-redecl.c:8:5: int extern [signed] [addressable] [toplevel] ret_const( ... )
+function-redecl.c:8:5: int extern [addressable] [signed] [toplevel] ret_const( ... )
function-redecl.c:13:13: error: symbol 'ret_as' redeclared with different type (different address spaces):
function-redecl.c:13:13: void <asn:1> *extern [addressable] [toplevel] ret_as( ... )
function-redecl.c:12:6: note: previously declared as:
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
*/