From 80e2b2a0fc0f39e92873c8462ec795b991f84ca7 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Tue, 19 May 2020 22:06:53 +0200 Subject: testsuite: add a few testcases for nested functions Sparse doesn't really support nested functions but is able to parse them correctly. Add some testcases with them so that it continue to catch possible errors concerning them. Signed-off-by: Luc Van Oostenryck --- validation/nested-functions.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 validation/nested-functions.c 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 + */ -- cgit 1.2.3-korg From efda1a044c7ce014f38c3225ec79b2befd0a0620 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 14 May 2020 16:33:48 +0200 Subject: misc: fix testcase typeof-safe This testcase was marked as known-to-fail but it was simply the expected error messages that were missing. So, slightly reorganize the test a little bit, add the expected messages and remove the 'known-to-fail' tag. Signed-off-by: Luc Van Oostenryck --- validation/typeof-safe.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) 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 */ -- cgit 1.2.3-korg From 62596fe4efb13a41cdc05b931792122f9d855d14 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 7 May 2020 17:46:04 +0200 Subject: misc: s/fntype/rettype/ In evaluate_return_expression(), it's checked if the type of the return statement match the function return type. But, the variable used to hold this type is named 'fntype' which is slightly confusing. So, rename the variable holding the return type to 'rettype' and only use 'fntype' for the one holding the full function type. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/evaluate.c b/evaluate.c index b7bb1f52..54cd5fa1 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3450,13 +3450,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) { + fntype = current_fn; + 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 +3469,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; } -- cgit 1.2.3-korg From 154278d4e26d9d48fbb5f2668533a4c4e598b564 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Tue, 21 Apr 2020 03:46:21 +0200 Subject: misc: always use the node for current_fn At evaluation time and at expansion time, current_fn is set to the function's base type (SYM_FN) but at parse time it's set to its parent type (SYM_NODE). Since current_fn is used to access the corresponding ident, it should be set to the node type, not the base. So, always set current_fn to the node type. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 4 ++-- expand.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/evaluate.c b/evaluate.c index 54cd5fa1..c18ae81d 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3422,7 +3422,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) @@ -3453,7 +3453,7 @@ static struct symbol *evaluate_return_expression(struct statement *stmt) struct symbol *fntype, *rettype; evaluate_expression(expr); - fntype = current_fn; + fntype = current_fn->ctype.base_type; rettype = fntype->ctype.base_type; if (!rettype || rettype == &void_ctype) { if (expr && expr->ctype != &void_ctype) 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; -- cgit 1.2.3-korg From aa42800cc4aca57bb29cdc90fcace6c352449078 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 9 Apr 2020 23:58:10 +0200 Subject: bad-goto: add testcase for 'jump inside discarded expression statement' A goto done into an piece of code discarded at expand or linearize time will produce an invalid IR. Add a testcase for it. Signed-off-by: Luc Van Oostenryck --- validation/label-stmt-expr1.c | 29 +++++++++++++++++++++++++++++ validation/linear/goto-and-expr-stmt0.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 validation/label-stmt-expr1.c create mode 100644 validation/linear/goto-and-expr-stmt0.c diff --git a/validation/label-stmt-expr1.c b/validation/label-stmt-expr1.c new file mode 100644 index 00000000..339217dc --- /dev/null +++ b/validation/label-stmt-expr1.c @@ -0,0 +1,29 @@ +static int foo(void) +{ + goto l; + ({ +l: + 0; + }); +} + +static void bar(void) +{ + ({ +l: + 0; + }); + goto l; +} + +/* + * check-name: label-stmt-expr1 + * check-known-to-fail + * + * 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/linear/goto-and-expr-stmt0.c b/validation/linear/goto-and-expr-stmt0.c new file mode 100644 index 00000000..54881353 --- /dev/null +++ b/validation/linear/goto-and-expr-stmt0.c @@ -0,0 +1,28 @@ +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-and-expr-stmt0 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: END + * check-error-ignore + */ -- cgit 1.2.3-korg From 0b6d161ed1cc0f2226482d64c56fe9dc89bc0ebf Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Fri, 10 Apr 2020 12:13:39 +0200 Subject: bad-goto: add testcases for linearization of invalid labels A goto to a reserved or a undeclared label will generate an IR with a branch to a non-existing BB. Bad. Add a testcase for these. Signed-off-by: Luc Van Oostenryck --- validation/linear/invalid-labels0.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 validation/linear/invalid-labels0.c diff --git a/validation/linear/invalid-labels0.c b/validation/linear/invalid-labels0.c new file mode 100644 index 00000000..ae3bf728 --- /dev/null +++ b/validation/linear/invalid-labels0.c @@ -0,0 +1,19 @@ +static void foo(void) +{ + goto return; +} + +void bar(void) +{ + goto neverland; +} + +/* + * check-name: invalid-labels0 + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * check-output-ignore + * check-output-excludes: END + * check-error-ignore + */ -- cgit 1.2.3-korg From 633a390c169082646e7172c753f319568ef22695 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 12 Apr 2020 09:26:22 +0200 Subject: bad-goto: reorganize testcases and add some more Reorganize the testcases related to the 'scope' of labels and add a few new ones. Also, some related testcases have some unreported errors other than the features being tested. This is a problem since such tescases can still fail after the feature being tested is fixed or implemented. So, fix these testcases or split them so that they each test a unique feature. Signed-off-by: Luc Van Oostenryck --- validation/__func__-scope.c | 8 +++ validation/asm-goto-labels.c | 22 +++++++ validation/asm-goto-lables.c | 22 ------- validation/discarded-label-statement.c | 24 ------- validation/label-asm.c | 1 + validation/label-attr.c | 2 +- validation/label-expr.c | 17 ----- validation/label-scope-cgoto.c | 83 ++++++++++++++++++++++++ validation/label-scope.c | 5 +- validation/label-scope1.c | 42 ++++++++++++ validation/label-scope2.c | 32 +++++++++ validation/label-stmt-expr0.c | 39 +++++++++++ validation/label-stmt-expr2.c | 47 ++++++++++++++ validation/label-unused.c | 24 +++++++ validation/linear/goto-and-expr-stmt0.c | 28 -------- validation/linear/goto-invalid.c | 19 ++++++ validation/linear/goto-stmt-expr-conditional.c | 28 ++++++++ validation/linear/goto-stmt-expr-short-circuit.c | 32 +++++++++ validation/linear/invalid-labels0.c | 19 ------ validation/linear/label-scope-cgoto.c | 11 ++++ validation/linear/label-stmt-dropped.c | 26 ++++++++ validation/linear/label-stmt-expr0.c | 17 +++++ validation/linear/label-unreachable.c | 20 ++++++ validation/linear/unreachable-label0.c | 19 ------ 24 files changed, 453 insertions(+), 134 deletions(-) create mode 100644 validation/__func__-scope.c create mode 100644 validation/asm-goto-labels.c delete mode 100644 validation/asm-goto-lables.c delete mode 100644 validation/discarded-label-statement.c delete mode 100644 validation/label-expr.c create mode 100644 validation/label-scope-cgoto.c create mode 100644 validation/label-scope1.c create mode 100644 validation/label-scope2.c create mode 100644 validation/label-stmt-expr0.c create mode 100644 validation/label-stmt-expr2.c create mode 100644 validation/label-unused.c delete mode 100644 validation/linear/goto-and-expr-stmt0.c create mode 100644 validation/linear/goto-invalid.c create mode 100644 validation/linear/goto-stmt-expr-conditional.c create mode 100644 validation/linear/goto-stmt-expr-short-circuit.c delete mode 100644 validation/linear/invalid-labels0.c create mode 100644 validation/linear/label-scope-cgoto.c create mode 100644 validation/linear/label-stmt-dropped.c create mode 100644 validation/linear/label-stmt-expr0.c create mode 100644 validation/linear/label-unreachable.c delete mode 100644 validation/linear/unreachable-label0.c 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-labels.c b/validation/asm-goto-labels.c new file mode 100644 index 00000000..ac2bf2ad --- /dev/null +++ b/validation/asm-goto-labels.c @@ -0,0 +1,22 @@ +static inline int __static_cpu_has(unsigned char bit) +{ + asm goto("1: jmp %l[t_no]\n" + "2:\n" + ".section .altinstructions,\"a\"\n" + "\n" + "1b\n" + "0\n" /* no replacement */ + " .byte %P0\n" /* feature bit */ + " .byte 2b - 1b\n" /* source len */ + " .byte 0\n" /* replacement len */ + " .byte 0xff + 0 - (2b-1b)\n" /* padding */ + ".previous\n" + : : "i" (bit) : : t_no, ble); + return 1; +t_no: + return 0; +} +/* + * check-name: Asm with goto labels. + */ + diff --git a/validation/asm-goto-lables.c b/validation/asm-goto-lables.c deleted file mode 100644 index ac2bf2ad..00000000 --- a/validation/asm-goto-lables.c +++ /dev/null @@ -1,22 +0,0 @@ -static inline int __static_cpu_has(unsigned char bit) -{ - asm goto("1: jmp %l[t_no]\n" - "2:\n" - ".section .altinstructions,\"a\"\n" - "\n" - "1b\n" - "0\n" /* no replacement */ - " .byte %P0\n" /* feature bit */ - " .byte 2b - 1b\n" /* source len */ - " .byte 0\n" /* replacement len */ - " .byte 0xff + 0 - (2b-1b)\n" /* padding */ - ".previous\n" - : : "i" (bit) : : t_no, ble); - return 1; -t_no: - return 0; -} -/* - * check-name: Asm with goto labels. - */ - diff --git a/validation/discarded-label-statement.c b/validation/discarded-label-statement.c deleted file mode 100644 index b4e58ac6..00000000 --- a/validation/discarded-label-statement.c +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Verify that the statement following an unused label - * is not discarded with the label. - */ - -static int bad(int a, int b) -{ - int r = 0; - -start: - r += a; - r += b; - - return r; -} - -/* - * check-name: discarded-label-statement - * check-command: test-linearize $file - * - * check-output-ignore - * check-output-contains: add - * check-output-contains: %arg1 - */ 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-expr.c b/validation/label-expr.c deleted file mode 100644 index e578ed00..00000000 --- a/validation/label-expr.c +++ /dev/null @@ -1,17 +0,0 @@ -int foo(void); -int foo(void) -{ - int r; - - r = ({ label: 1; }); - return r; -} - -/* - * check-name: label-expr - * check-command: test-linearize $file - * check-output-ignore - * - * check-output-excludes: ret\\.32\$ - * check-output-contains: ret\\.32 *\\$1 - */ diff --git a/validation/label-scope-cgoto.c b/validation/label-scope-cgoto.c new file mode 100644 index 00000000..c5d278d3 --- /dev/null +++ b/validation/label-scope-cgoto.c @@ -0,0 +1,83 @@ +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-known-to-fail + * + * 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..8c04ac65 --- /dev/null +++ b/validation/label-scope2.c @@ -0,0 +1,32 @@ +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-known-to-fail + * + * 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..66a64902 --- /dev/null +++ b/validation/label-stmt-expr0.c @@ -0,0 +1,39 @@ +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-known-to-fail + * + * 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-expr2.c b/validation/label-stmt-expr2.c new file mode 100644 index 00000000..7a38e379 --- /dev/null +++ b/validation/label-stmt-expr2.c @@ -0,0 +1,47 @@ +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-known-to-fail + * + * 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..c136c7a8 --- /dev/null +++ b/validation/label-unused.c @@ -0,0 +1,24 @@ +static void foo(void) +{ +l: + return; +} + +static int bar(void) +{ + return ({ +l: + ; + 0; + }); +} + +/* + * check-name: label-unused + * check-known-to-fail + * + * 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-and-expr-stmt0.c b/validation/linear/goto-and-expr-stmt0.c deleted file mode 100644 index 54881353..00000000 --- a/validation/linear/goto-and-expr-stmt0.c +++ /dev/null @@ -1,28 +0,0 @@ -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-and-expr-stmt0 - * check-command: test-linearize -Wno-decl $file - * check-known-to-fail - * - * check-output-ignore - * check-output-excludes: END - * check-error-ignore - */ diff --git a/validation/linear/goto-invalid.c b/validation/linear/goto-invalid.c new file mode 100644 index 00000000..569d0b0d --- /dev/null +++ b/validation/linear/goto-invalid.c @@ -0,0 +1,19 @@ +static void foo(void) +{ + goto return; +} + +void bar(void) +{ + goto neverland; +} + +/* + * check-name: goto-invalid + * check-command: test-linearize -Wno-decl $file + * check-known-to-fail + * + * 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..6576052b --- /dev/null +++ b/validation/linear/goto-stmt-expr-conditional.c @@ -0,0 +1,28 @@ +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-known-to-fail + * + * 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..426315e6 --- /dev/null +++ b/validation/linear/goto-stmt-expr-short-circuit.c @@ -0,0 +1,32 @@ +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-known-to-fail + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/linear/invalid-labels0.c b/validation/linear/invalid-labels0.c deleted file mode 100644 index ae3bf728..00000000 --- a/validation/linear/invalid-labels0.c +++ /dev/null @@ -1,19 +0,0 @@ -static void foo(void) -{ - goto return; -} - -void bar(void) -{ - goto neverland; -} - -/* - * check-name: invalid-labels0 - * check-command: test-linearize -Wno-decl $file - * check-known-to-fail - * - * check-output-ignore - * check-output-excludes: END - * check-error-ignore - */ diff --git a/validation/linear/label-scope-cgoto.c b/validation/linear/label-scope-cgoto.c new file mode 100644 index 00000000..592f1ce4 --- /dev/null +++ b/validation/linear/label-scope-cgoto.c @@ -0,0 +1,11 @@ +#include + +/* + * check-name: linear/label-scope-cgoto + * check-command: test-linearize -Wno-decl -I. $file + * check-known-to-fail + * + * check-error-ignore + * check-output-ignore + * check-output-excludes: END + */ diff --git a/validation/linear/label-stmt-dropped.c b/validation/linear/label-stmt-dropped.c new file mode 100644 index 00000000..74e0f2e6 --- /dev/null +++ b/validation/linear/label-stmt-dropped.c @@ -0,0 +1,26 @@ +/* + * Verify that the statement following an unused label + * is not discarded with the label. + */ + +static int bad(int a, int b) +{ + int r = 0; + +start: + r += a; + r += b; + + if (!r) + goto start; + return r; +} + +/* + * check-name: label-stmt-dropped + * check-command: test-linearize $file + * + * check-output-ignore + * check-output-contains: add + * check-output-contains: %arg1 + */ diff --git a/validation/linear/label-stmt-expr0.c b/validation/linear/label-stmt-expr0.c new file mode 100644 index 00000000..ff3c0980 --- /dev/null +++ b/validation/linear/label-stmt-expr0.c @@ -0,0 +1,17 @@ +int foo(void); +int foo(void) +{ + int r; + + r = ({ goto label; label: 1; }); + return r; +} + +/* + * check-name: label-stmt-expr0 + * check-command: test-linearize $file + * check-output-ignore + * + * check-output-excludes: ret\\.32\$ + * check-output-contains: ret\\.32 *\\$1 + */ diff --git a/validation/linear/label-unreachable.c b/validation/linear/label-unreachable.c new file mode 100644 index 00000000..a44e1211 --- /dev/null +++ b/validation/linear/label-unreachable.c @@ -0,0 +1,20 @@ +static int foo(int a) +{ + goto label; + switch(a) { + default: +label: + break; + } + return 0; +} + +/* + * 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/linear/unreachable-label0.c b/validation/linear/unreachable-label0.c deleted file mode 100644 index 695e5cb0..00000000 --- a/validation/linear/unreachable-label0.c +++ /dev/null @@ -1,19 +0,0 @@ -static int foo(int a) -{ - goto label; - switch(a) { - default: -label: - break; - } - return 0; -} - -/* - * check-name: unreachable-label0 - * check-command: test-linearize $file - * - * check-output-ignore - * check-output-contains: ret\\. - * check-output-excludes: END - */ -- cgit 1.2.3-korg From 557aba56c4bc4c4b4c38b97a2d545c31e0706b31 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 11 Apr 2020 11:19:31 +0200 Subject: bad-goto: do not linearize if the IR will be invalid In some error cases, it's not possible to produce a valid & correct IR for the concerned function. For exemple, if the AST contains invalid gotos, the CFG will either be invalid or won't correspond to the erroneous source code. So, refuse to linearize such functions. Signed-off-by: Luc Van Oostenryck --- linearize.c | 2 +- symbol.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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/symbol.h b/symbol.h index 7241f13d..50dba78a 100644 --- a/symbol.h +++ b/symbol.h @@ -171,6 +171,7 @@ struct symbol { unsigned long offset; int bit_size; unsigned int bit_offset:8, + bogus_linear:1, variadic:1, initialized:1, examined:1, -- cgit 1.2.3-korg From 4a71ea1810242c08f66afe16ef9ec0b917439023 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 12 Apr 2020 10:37:58 +0200 Subject: bad-goto: reorg test in evaluate_goto_statement() No functional changes here, only changing the code structure to prepare more incoming changes. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/evaluate.c b/evaluate.c index c18ae81d..4bdd5ed0 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3742,10 +3742,14 @@ 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)) + if (!label) { + // no label associated, may be a computed goto + evaluate_expression(stmt->goto_expression); + return; + } + if (!label->stmt && label->ident && !lookup_keyword(label->ident, NS_KEYWORD)) { sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); - - evaluate_expression(stmt->goto_expression); + } } struct symbol *evaluate_statement(struct statement *stmt) -- cgit 1.2.3-korg From 86edd142e6fa4483cf5cb057ffac4cb548a9374c Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 12 Apr 2020 10:37:58 +0200 Subject: bad-goto: simplify testing of undeclared labels There is no need to do a lookup: checking if the label's symbol is in the NS_LABEL namespace and is lacking an associated statement is enough and much simpler. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evaluate.c b/evaluate.c index 4bdd5ed0..d4b46227 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3747,7 +3747,8 @@ static void evaluate_goto_statement(struct statement *stmt) evaluate_expression(stmt->goto_expression); return; } - if (!label->stmt && label->ident && !lookup_keyword(label->ident, NS_KEYWORD)) { + + if (label->namespace == NS_LABEL && !label->stmt) { sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); } } -- cgit 1.2.3-korg From 4e70521d74cb10b795825f7376539ed493dd2ac3 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 11 Apr 2020 11:19:31 +0200 Subject: bad-goto: do not linearize function with undeclared labels It's not possible to produce a valid & correct IR if the function contains a goto to an undeclared label. So, try to catch these situations and mark the function as such, the linearization will then simply ignore it. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 1 + 1 file changed, 1 insertion(+) diff --git a/evaluate.c b/evaluate.c index d4b46227..c757fc82 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3750,6 +3750,7 @@ static void evaluate_goto_statement(struct statement *stmt) if (label->namespace == NS_LABEL && !label->stmt) { sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); + current_fn->bogus_linear = 1; } } -- cgit 1.2.3-korg From 38e7c70c3074caab4c5aa68c0cee299a71537bee Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 11 Apr 2020 12:11:17 +0200 Subject: bad-goto: catch labels with reserved names If a reserved name is used as the destination of a goto, its associated label won't be valid and at linearization time no BB will can be created for it, resulting in an invalid IR. So, catch such gotos at evaluation time and mark the function to not be linearized. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 2 ++ validation/linear/goto-invalid.c | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/evaluate.c b/evaluate.c index c757fc82..21d5d761 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3752,6 +3752,8 @@ static void evaluate_goto_statement(struct statement *stmt) sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); current_fn->bogus_linear = 1; } + if (label->namespace == NS_NONE) + current_fn->bogus_linear = 1; } struct symbol *evaluate_statement(struct statement *stmt) diff --git a/validation/linear/goto-invalid.c b/validation/linear/goto-invalid.c index 569d0b0d..860b32a4 100644 --- a/validation/linear/goto-invalid.c +++ b/validation/linear/goto-invalid.c @@ -11,7 +11,6 @@ void bar(void) /* * check-name: goto-invalid * check-command: test-linearize -Wno-decl $file - * check-known-to-fail * * check-error-ignore * check-output-ignore -- cgit 1.2.3-korg From 06c0531322a54e532eb3b9155bbf9f51adca0492 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Wed, 1 Apr 2020 13:19:07 +0200 Subject: scope: no memset() needed after __alloc_scope() When starting some scopes, the newly allocated struct is memset'ed with zero but this is unneeded since the allocator always returns zeroed memory. Remove the unneeded call to memset(). Signed-off-by: Luc Van Oostenryck --- scope.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/scope.c b/scope.c index 420c0f5a..0e4fb3b4 100644 --- a/scope.c +++ b/scope.c @@ -68,7 +68,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 +76,6 @@ void start_file_scope(void) { struct scope *scope = __alloc_scope(0); - memset(scope, 0, sizeof(*scope)); scope->next = &builtin_scope; file_scope = scope; -- cgit 1.2.3-korg From 93ec535db78b56aa1dda92f5754126caff1d6ef6 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Wed, 1 Apr 2020 16:28:28 +0200 Subject: scope: move scope opening/ending inside compound_statement() A compound statement starts and ends a block scope, so it's better to start & end this scope inside the function parsing the statement: compound_statement. The only exception is for the body of a function where the scope also enclose the parameter declaration but that is fine since the function is special anyway. Move the calls to start & close the block scope inside compound_statement() and directly call statement_list() for the function body. Signed-off-by: Luc Van Oostenryck --- expression.c | 2 -- parse.c | 13 ++++++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/expression.c b/expression.c index 5b9bddfe..78e577cf 100644 --- a/expression.c +++ b/expression.c @@ -71,9 +71,7 @@ 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(); token = compound_statement(token->next, stmt); - end_symbol_scope(); token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); diff --git a/parse.c b/parse.c index 9e7b74f9..e23c5b64 100644 --- a/parse.c +++ b/parse.c @@ -2555,11 +2555,7 @@ static struct token *statement(struct token *token, struct statement **tree) } 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"); } @@ -2666,7 +2662,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_symbol_scope(); token = statement_list(token, &stmt->stmts); + end_symbol_scope(); return token; } @@ -2818,15 +2817,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); -- cgit 1.2.3-korg From 91de00e92952a61a745be95c56e8595b506d866a Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 18 Apr 2020 16:32:41 +0200 Subject: scope: extract bind_symbol_with_scope() from bind_symbol() In most cases, the scope that must be used for a symbol is given by its namespace. However, in some situations a different scope must be used. This is then set, for example by doing the lookup with the wrong namespace (but corresponding to the desired scope) and changing it just after to its correct value. To avoid these contortions, extract from bind_symbol() a version where the scope can be explicitly given: bind_symbol_with_scope(). Signed-off-by: Luc Van Oostenryck --- symbol.c | 13 +++++++++---- symbol.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) 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 50dba78a..c297c778 100644 --- a/symbol.h +++ b/symbol.h @@ -332,6 +332,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 *); -- cgit 1.2.3-korg From c1b89f892ebef693da906685cd72c39f1f38c8ff Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 18 Apr 2020 16:37:32 +0200 Subject: scope: __func__ is special __func__ needs to be in the namepsace for symbols: NS_SYMBOL but doesn't follow the usual scope rules of them: it always needs to be declared in the function scope. So, use bind_symbol_with_scope() instead of first using bind_symbol() and then changing the namespace. Also change the comment to better express that it's the scope that is the unusual thing. Signed-off-by: Luc Van Oostenryck --- expression.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/expression.c b/expression.c index 78e577cf..ffb3c2dc 100644 --- a/expression.c +++ b/expression.c @@ -122,9 +122,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); -- cgit 1.2.3-korg From 5e8b78ce213f2110343a0a6c10a28a3c6797314e Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sat, 18 Apr 2020 16:37:47 +0200 Subject: scope: __label__ is special Labels declared wth __label__ are special because they must follow the block scope normally used for variables instad of using the scope used for labels. So, use bind_symbol_with_scope() instead of first using bind_symbol() and then changing the namespace. Signed-off-by: Luc Van Oostenryck --- parse.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/parse.c b/parse.c index e23c5b64..29e3f939 100644 --- a/parse.c +++ b/parse.c @@ -2569,8 +2569,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, ',')) -- cgit 1.2.3-korg From dbaf79f0e3abc46002fbf918240e1c09c6e4697a Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Wed, 1 Apr 2020 16:01:45 +0200 Subject: scope: s/{start,end}_symbol_scope/{start,end}_block_scope/ The functions {start,end}_symbol_scope() haven't been renamed when separated scope have been introduced for functions & blocks. But these functions only start & end a block scope. So, rename them to their more direct name: {start,end}_block_scope(). Signed-off-by: Luc Van Oostenryck --- parse.c | 12 ++++++------ scope.c | 4 ++-- scope.h | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/parse.c b/parse.c index 29e3f939..ecc33765 100644 --- a/parse.c +++ b/parse.c @@ -2230,7 +2230,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); @@ -2245,7 +2245,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) @@ -2290,7 +2290,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); @@ -2310,7 +2310,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) @@ -2662,9 +2662,9 @@ 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_symbol_scope(); + start_block_scope(); token = statement_list(token, &stmt->stmts); - end_symbol_scope(); + end_block_scope(); return token; } diff --git a/scope.c b/scope.c index 0e4fb3b4..cc54f1e1 100644 --- a/scope.c +++ b/scope.c @@ -84,7 +84,7 @@ void start_file_scope(void) block_scope = scope; } -void start_symbol_scope(void) +void start_block_scope(void) { start_scope(&block_scope); } @@ -129,7 +129,7 @@ void new_file_scope(void) start_file_scope(); } -void end_symbol_scope(void) +void end_block_scope(void) { end_scope(&block_scope); } diff --git a/scope.h b/scope.h index 3cad514a..83741459 100644 --- a/scope.h +++ b/scope.h @@ -47,8 +47,8 @@ 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); -- cgit 1.2.3-korg From db0a795b145501e456cac5180be03ef697810845 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Wed, 1 Apr 2020 15:45:33 +0200 Subject: scope: let labels have their own scope It's invalid to jump inside a statement expression. So, concerning labels & gotos, a statement expression is like a kind of scope. So, in preparation for the detection of such jumps, create these new scopes and open/close them when entering/leaving statement expressions. Signed-off-by: Luc Van Oostenryck --- expression.c | 2 ++ scope.c | 17 +++++++++++++++-- scope.h | 4 ++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/expression.c b/expression.c index ffb3c2dc..f8a8f03e 100644 --- a/expression.c +++ b/expression.c @@ -71,7 +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_label_scope(); token = compound_statement(token->next, stmt); + end_label_scope(); token = expect(token, '}', "at end of statement expression"); } else token = parse_expression(token, expr); diff --git a/scope.c b/scope.c index cc54f1e1..75ee19cf 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 @@ -91,8 +92,9 @@ void start_block_scope(void) 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) @@ -137,7 +139,18 @@ void end_block_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) +{ + end_scope(&label_scope); } int is_outer_scope(struct scope *scope) diff --git a/scope.h b/scope.h index 83741459..ddcb90bd 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; @@ -53,6 +54,9 @@ 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 *); -- cgit 1.2.3-korg From eff5e6c1a4e6c36614dcfaa0213e2ccf57e844a3 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 2 Apr 2020 14:43:58 +0200 Subject: scope: add is_in_scope() Add an helper to check if a scope is included into another one. Signed-off-by: Luc Van Oostenryck --- scope.c | 9 +++++++++ scope.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/scope.c b/scope.c index 75ee19cf..635e0d6d 100644 --- a/scope.c +++ b/scope.c @@ -162,3 +162,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 ddcb90bd..36a56d6a 100644 --- a/scope.h +++ b/scope.h @@ -62,4 +62,6 @@ 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 -- cgit 1.2.3-korg From 177f5fe2255e2fd16040c802a7298ec21a29bd80 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 9 Apr 2020 09:14:01 +0200 Subject: scope: give a scope for labels & gotos One way of detecting gotos inside an statement expression is to use a new kind of scope for the gotos & labels. Since gotos don't need to have their label predeclared, nothing can be checked at parsing time but later it can be checked that a goto doesn't jump inside one of the label scope created by statement expressions. So, add additional scope information to gotos and labels to allow such check to be done. Note: the label's symbols are still created in the function scope since they belong to a single namespace. Signed-off-by: Luc Van Oostenryck --- parse.c | 8 +++++++- parse.h | 1 + symbol.h | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/parse.c b/parse.c index ecc33765..bf45e3b0 100644 --- a/parse.c +++ b/parse.c @@ -2488,7 +2488,12 @@ 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); + stmt->goto_label = label; + if (!label->stmt && !label->label_scope) { + label->label_scope = label_scope; + label->label_pos = stmt->pos; + } token = token->next; } else { sparse_error(token->pos, "Expected identifier or goto expression"); @@ -2549,6 +2554,7 @@ static struct token *statement(struct token *token, struct statement **tree) } stmt->type = STMT_LABEL; stmt->label_identifier = s; + stmt->label_scope = label_scope; s->stmt = stmt; return statement(token, &stmt->label_statement); } diff --git a/parse.h b/parse.h index 0742a2a8..daef2439 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 */ { diff --git a/symbol.h b/symbol.h index c297c778..2293d06d 100644 --- a/symbol.h +++ b/symbol.h @@ -167,6 +167,10 @@ struct symbol { int (*handler)(struct stream *, struct token **, struct token *); int normal; }; + struct /* NS_LABEL */ { + struct scope *label_scope; + struct position label_pos; + }; struct /* NS_SYMBOL */ { unsigned long offset; int bit_size; -- cgit 1.2.3-korg From 33319e351c2874ca7c187bd96e7d986794fd2b41 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 26 Apr 2020 22:45:06 +0200 Subject: bad-goto: jumping inside a statement expression is an error It's invalid to jump inside a statement expression. So, detect such jumps, issue an error message and mark the function as useless for linearization since the resulting IR would be invalid. Signed-off-by: Luc Van Oostenryck --- parse.c | 30 ++++++++++++++++++++---- parse.h | 1 + validation/label-scope2.c | 1 - validation/label-stmt-expr0.c | 1 - validation/label-stmt-expr1.c | 1 - validation/label-stmt-expr2.c | 1 - validation/linear/goto-stmt-expr-conditional.c | 1 - validation/linear/goto-stmt-expr-short-circuit.c | 1 - 8 files changed, 27 insertions(+), 10 deletions(-) diff --git a/parse.c b/parse.c index bf45e3b0..b9d4940e 100644 --- a/parse.c +++ b/parse.c @@ -2480,6 +2480,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; @@ -2490,10 +2511,7 @@ static struct token *parse_goto_statement(struct token *token, struct statement } else if (token_type(token) == TOKEN_IDENT) { struct symbol *label = label_symbol(token); stmt->goto_label = label; - if (!label->stmt && !label->label_scope) { - label->label_scope = label_scope; - label->label_pos = stmt->pos; - } + check_label_usage(label, stmt->pos); token = token->next; } else { sparse_error(token->pos, "Expected identifier or goto expression"); @@ -2555,6 +2573,10 @@ 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); } diff --git a/parse.h b/parse.h index daef2439..2cfdd872 100644 --- a/parse.h +++ b/parse.h @@ -125,6 +125,7 @@ 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 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/validation/label-scope2.c b/validation/label-scope2.c index 8c04ac65..44864752 100644 --- a/validation/label-scope2.c +++ b/validation/label-scope2.c @@ -23,7 +23,6 @@ a: /* * check-name: label-scope2 - * check-known-to-fail * * check-error-start label-scope2.c:20:17: error: label 'a' used outside statement expression diff --git a/validation/label-stmt-expr0.c b/validation/label-stmt-expr0.c index 66a64902..5fc452ab 100644 --- a/validation/label-stmt-expr0.c +++ b/validation/label-stmt-expr0.c @@ -26,7 +26,6 @@ l: 1; /* * check-name: label-stmt-expr0 * check-command: sparse -Wno-decl $file - * check-known-to-fail * * check-error-start label-stmt-expr0.c:6:9: error: label 'l' used outside statement expression diff --git a/validation/label-stmt-expr1.c b/validation/label-stmt-expr1.c index 339217dc..32a89aad 100644 --- a/validation/label-stmt-expr1.c +++ b/validation/label-stmt-expr1.c @@ -18,7 +18,6 @@ l: /* * check-name: label-stmt-expr1 - * check-known-to-fail * * check-error-start label-stmt-expr1.c:3:9: error: label 'l' used outside statement expression diff --git a/validation/label-stmt-expr2.c b/validation/label-stmt-expr2.c index 7a38e379..8c54477a 100644 --- a/validation/label-stmt-expr2.c +++ b/validation/label-stmt-expr2.c @@ -30,7 +30,6 @@ l: /* * check-name: label-stmt-expr2 - * check-known-to-fail * * check-error-start label-stmt-expr2.c:3:9: error: label 'l' used outside statement expression diff --git a/validation/linear/goto-stmt-expr-conditional.c b/validation/linear/goto-stmt-expr-conditional.c index 6576052b..bbfcb3eb 100644 --- a/validation/linear/goto-stmt-expr-conditional.c +++ b/validation/linear/goto-stmt-expr-conditional.c @@ -20,7 +20,6 @@ a: /* * check-name: goto-stmt-expr-conditional * check-command: test-linearize -Wno-decl $file - * check-known-to-fail * * check-error-ignore * check-output-ignore diff --git a/validation/linear/goto-stmt-expr-short-circuit.c b/validation/linear/goto-stmt-expr-short-circuit.c index 426315e6..a5953e73 100644 --- a/validation/linear/goto-stmt-expr-short-circuit.c +++ b/validation/linear/goto-stmt-expr-short-circuit.c @@ -24,7 +24,6 @@ inside: /* * check-name: goto-stmt-expr-short-circuit * check-command: test-linearize -Wno-decl $file - * check-known-to-fail * * check-error-ignore * check-output-ignore -- cgit 1.2.3-korg From 25216452d77c39fcb258ff96d44f4f8d55bbda43 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Sun, 26 Apr 2020 22:45:06 +0200 Subject: bad-goto: label expression inside a statement expression is UB More exactly, what is undefined is to jump inside the statement expression with a computed goto. Of course, once the address of such a label is taken, it's generaly impossible to track if it will be used or not to jump inside the statement expression. So, for now, handle taking the address of such a label from outside the statement expression, exactly as if a computed goto is effectively done from there and so issue an error message and also mark the function as useless for linearization. Note: this is only partially correct since: 1) the address could be taken from outside the statement and never used for a computed goto. 2) the address could be taken from outside the statement but the corresponding computed goto only done from inside, which is perfectly fine. 3) the address could be taken from inside but a computed goto done from outside. Note: the real problem, like for the regular goto, is that the statement expression can be eliminated before linearization, the correspondng gotos corresponding then to branches to unexistent BBs. Signed-off-by: Luc Van Oostenryck --- expression.c | 1 + 1 file changed, 1 insertion(+) diff --git a/expression.c b/expression.c index f8a8f03e..bbbc24e6 100644 --- a/expression.c +++ b/expression.c @@ -691,6 +691,7 @@ static struct token *unary_expression(struct token *token, struct expression **t 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; -- cgit 1.2.3-korg From c5421b0d1ab0ab2d182ee1c1efbbc20f24275242 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Mon, 27 Apr 2020 02:46:46 +0200 Subject: bad-goto: extract check_label_declaration() Extract this helper from evaluate_goto_statement(). Signed-off-by: Luc Van Oostenryck --- evaluate.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/evaluate.c b/evaluate.c index 21d5d761..b272e3f6 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) @@ -3748,12 +3763,7 @@ static void evaluate_goto_statement(struct statement *stmt) return; } - if (label->namespace == NS_LABEL && !label->stmt) { - sparse_error(stmt->pos, "label '%s' was not declared", show_ident(label->ident)); - current_fn->bogus_linear = 1; - } - if (label->namespace == NS_NONE) - current_fn->bogus_linear = 1; + check_label_declaration(stmt->pos, label); } struct symbol *evaluate_statement(struct statement *stmt) -- cgit 1.2.3-korg From f3ec4bb41cddaeb154f0b8a7ffbc1d7d64691263 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Mon, 27 Apr 2020 02:47:47 +0200 Subject: bad-goto: check declaration of label expressions Issue an error when taking the address of an undeclared label and mark the function as improper for linearization since the resulting IR would be invalid. Signed-off-by: Luc Van Oostenryck --- evaluate.c | 1 + validation/label-scope-cgoto.c | 1 - validation/linear/label-scope-cgoto.c | 1 - 3 files changed, 1 insertion(+), 2 deletions(-) diff --git a/evaluate.c b/evaluate.c index b272e3f6..63d75d90 100644 --- a/evaluate.c +++ b/evaluate.c @@ -3344,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: diff --git a/validation/label-scope-cgoto.c b/validation/label-scope-cgoto.c index c5d278d3..1edb9948 100644 --- a/validation/label-scope-cgoto.c +++ b/validation/label-scope-cgoto.c @@ -65,7 +65,6 @@ l: 1; /* * check-name: label-scope-cgoto * check-command: sparse -Wno-decl $file - * check-known-to-fail * * check-error-start label-scope-cgoto.c:12:19: error: label 'l' used outside statement expression diff --git a/validation/linear/label-scope-cgoto.c b/validation/linear/label-scope-cgoto.c index 592f1ce4..0eba05ae 100644 --- a/validation/linear/label-scope-cgoto.c +++ b/validation/linear/label-scope-cgoto.c @@ -3,7 +3,6 @@ /* * check-name: linear/label-scope-cgoto * check-command: test-linearize -Wno-decl -I. $file - * check-known-to-fail * * check-error-ignore * check-output-ignore -- cgit 1.2.3-korg From b307a6b54da61e462c726619dc45b2f19e850c6d Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Tue, 5 May 2020 17:55:41 +0200 Subject: bad-label: check for unused labels Issue a warning if a label is defined but not used. Note: this should take in account the attribute 'unused'. Signed-off-by: Luc Van Oostenryck --- scope.c | 8 ++++++++ validation/label-unused.c | 1 - 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/scope.c b/scope.c index 635e0d6d..3a0d784f 100644 --- a/scope.c +++ b/scope.c @@ -150,6 +150,14 @@ void start_label_scope(void) void end_label_scope(void) { + struct symbol *sym; + + FOR_EACH_PTR(label_scope->symbols, sym) { + if (!sym->stmt || sym->used) + continue; + warning(sym->pos, "unused label '%s'", show_ident(sym->ident)); + } END_FOR_EACH_PTR(sym); + end_scope(&label_scope); } diff --git a/validation/label-unused.c b/validation/label-unused.c index c136c7a8..a654ef77 100644 --- a/validation/label-unused.c +++ b/validation/label-unused.c @@ -15,7 +15,6 @@ l: /* * check-name: label-unused - * check-known-to-fail * * check-error-start label-unused.c:3:1: warning: unused label 'l' -- cgit 1.2.3-korg From d89355c8ec5761eecc18a46c48f254feee8c9234 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Thu, 7 May 2020 17:26:40 +0200 Subject: bad-label: mark labels as used when needed In most cases symbols are automatically marked as being used via a successfull call to lookup_symbols(), the idea being that the symbol will be created at its declaration and then any (successfull) lookup will correspond to an use. For labels, things are slightly different because labels are created on-demand via label_symbol() and their use can precede their 'declaration'. And of, course, label_symbol() has no ways to know if it is used for a definition or an use. So, fix this by adding an argument to label_symbol(), explictly telling if the call correspond to an use or not. Signed-off-by: Luc Van Oostenryck --- expression.c | 2 +- parse.c | 10 ++++++---- parse.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/expression.c b/expression.c index bbbc24e6..99a6d756 100644 --- a/expression.c +++ b/expression.c @@ -686,7 +686,7 @@ 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); diff --git a/parse.c b/parse.c index b9d4940e..a8e4a02e 100644 --- a/parse.c +++ b/parse.c @@ -726,12 +726,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; @@ -2139,7 +2141,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, ',')); @@ -2509,7 +2511,7 @@ 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) { - struct symbol *label = label_symbol(token); + struct symbol *label = label_symbol(token, 1); stmt->goto_label = label; check_label_usage(label, stmt->pos); token = token->next; @@ -2563,7 +2565,7 @@ 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); + struct symbol *s = label_symbol(token, 0); token = skip_attributes(token->next->next); if (s->stmt) { sparse_error(stmt->pos, "label '%s' redefined", show_ident(s->ident)); diff --git a/parse.h b/parse.h index 2cfdd872..5ac9a23b 100644 --- a/parse.h +++ b/parse.h @@ -124,7 +124,7 @@ 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 *); -- cgit 1.2.3-korg From 384008074a8d8881fefaa4fdda330120385d1259 Mon Sep 17 00:00:00 2001 From: Luc Van Oostenryck Date: Wed, 13 May 2020 22:32:04 +0200 Subject: bad-label: respect attribute((unused)) Currently, attributes on labels were simply ignored. This was fine since nothing was done wth them anyway. But now that Sparse can give a warning for unused labels it would be nice to also support the attribute 'unused' not to issues the warning when not desired. So, add a small helper around handle_attributes() and use this instead of skipping the attributes. Signed-off-by: Luc Van Oostenryck --- parse.c | 11 ++++++++++- scope.c | 2 ++ symbol.h | 1 + validation/label-unused.c | 6 ++++++ 4 files changed, 19 insertions(+), 1 deletion(-) diff --git a/parse.c b/parse.c index a8e4a02e..3cd84a3c 100644 --- a/parse.c +++ b/parse.c @@ -2554,6 +2554,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); @@ -2566,7 +2575,7 @@ static struct token *statement(struct token *token, struct statement **tree) if (match_op(token->next, ':')) { struct symbol *s = label_symbol(token, 0); - token = skip_attributes(token->next->next); + 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 diff --git a/scope.c b/scope.c index 3a0d784f..fb587c47 100644 --- a/scope.c +++ b/scope.c @@ -155,6 +155,8 @@ void end_label_scope(void) 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); diff --git a/symbol.h b/symbol.h index 2293d06d..6f904795 100644 --- a/symbol.h +++ b/symbol.h @@ -170,6 +170,7 @@ struct symbol { struct /* NS_LABEL */ { struct scope *label_scope; struct position label_pos; + unsigned long label_modifiers; }; struct /* NS_SYMBOL */ { unsigned long offset; diff --git a/validation/label-unused.c b/validation/label-unused.c index a654ef77..e3f255e1 100644 --- a/validation/label-unused.c +++ b/validation/label-unused.c @@ -13,6 +13,12 @@ l: }); } +static void baz(void) +{ +l: __attribute__((unused)); + return; +} + /* * check-name: label-unused * -- cgit 1.2.3-korg