diff options
| author | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2018-02-08 12:49:03 +0100 |
|---|---|---|
| committer | Luc Van Oostenryck <luc.vanoostenryck@gmail.com> | 2018-02-08 12:49:03 +0100 |
| commit | a695e053a24db2f092b76c7394ca57899b43e345 (patch) | |
| tree | dd6da172ad272610f6569ca03061c1195ddd0b0a | |
| parent | 6b91f6997c7debd4eaa03b8f9e222aaafa937498 (diff) | |
| parent | 047a43fa754c0f82666d5858168bd63fe87d90a7 (diff) | |
| download | sparse-dev-a695e053a24db2f092b76c7394ca57899b43e345.tar.gz | |
Merge branches 'cse-setfval' and 'extract-eval' into tip
| -rw-r--r-- | Documentation/data-structures.txt | 107 | ||||
| -rw-r--r-- | Documentation/test-suite | 8 | ||||
| -rw-r--r-- | evaluate.c | 70 | ||||
| -rw-r--r-- | lib.c | 1 | ||||
| -rw-r--r-- | linearize.c | 4 | ||||
| -rw-r--r-- | simplify.c | 241 | ||||
| -rw-r--r-- | sparse.1 | 7 | ||||
| -rw-r--r-- | symbol.c | 4 | ||||
| -rw-r--r-- | symbol.h | 5 | ||||
| -rw-r--r-- | validation/bad-type-twice0.c | 13 | ||||
| -rw-r--r-- | validation/bad-type-twice1.c | 16 | ||||
| -rw-r--r-- | validation/bad-type-twice2.c | 18 | ||||
| -rw-r--r-- | validation/ptr-sub-blows.c | 23 | ||||
| -rw-r--r-- | validation/typeof-bad.c | 17 |
14 files changed, 331 insertions, 203 deletions
diff --git a/Documentation/data-structures.txt b/Documentation/data-structures.txt index 3745b54c..14419263 100644 --- a/Documentation/data-structures.txt +++ b/Documentation/data-structures.txt @@ -1,54 +1,55 @@ +- A slightly edited irc discussion with Josh Triplett. +- Describes most data structures used in sparse. -<JoshTriplett> As far as the parsing structures go... -<JoshTriplett> The C parser exists in two main files: parse.c, which parses statements, and expression.c, which parses expressions. -<JoshTriplett> parse.h contains the definition of struct statement, which represents a C statement. -<JoshTriplett> That includes only those things which can't appear as an expression, which primarily includes control flow statements such as if, loops, switch/case, and goto. -<JoshTriplett> expression.h contains the definition of struct expression, which represents a C expression. That has a lot more content, since most C constructs can appear in expressions. -<JoshTriplett> A series of statements forms a compound statement (STMT_COMPOUND). -<JoshTriplett> That appears as another struct statement which has a statement_list member. -<JoshTriplett> A function body consists of a compound statement. -<JoshTriplett> When you look at a loop body, if or else body, or case body, you'll notice that they just have a struct statement, not a statement_list; they can have multiple statements by using a compound statement. -<JoshTriplett> Also note that all loops get turned into a single "iterator" statement. -<JoshTriplett> for, while, and do-while. -<JoshTriplett> A symbol, then, represents a name in a C file. A symbol might represent a variable, a function, a label, or various other things. -<JoshTriplett> See symbol.h. -<JoshTriplett> "struct symbol" represents one symbol. -<JoshTriplett> As with the various other structures, it has some common data and a union of sub-structures for the parts that differ between different types. -<JoshTriplett> Most of the interesting bits come in the NS_SYMBOL case. -<JoshTriplett> Among other things, it has a struct statement for the body of a function (if any), a list of symbols for the arguments, an expression for a variable initializer, and so on. -<JoshTriplett> Together, struct symbol, struct statement, and struct expression represent most of the abstract syntax tree for C. -<JoshTriplett> So, that represents most of the "front-end" of Sparse: parsing C and generating that abstract syntax tree. -<JoshTriplett> That much occurs in pretty much any program using the Sparse frontend. -<JoshTriplett> The backend varies among programs. -<JoshTriplett> For instance, the c2xml backend goes that far, then outputs XML. -<JoshTriplett> The sparse static analysis backend has a few steps: it generates linearized bytecode, does some evaluation on that, and outputs some warnings. -<JoshTriplett> Several other backends run that linearized bytecode stage. -<JoshTriplett> The linearized bytecode itself has a set of nested structures. -<JoshTriplett> linearize.h defines all of them. -<JoshTriplett> At the top level, it has struct entrypoint. -<JoshTriplett> That represents an entrypoint to the code, which would normally mean a function. -<JoshTriplett> An entrypoint has a list of basic blocks. -<JoshTriplett> struct basic_block. -<JoshTriplett> A basic block represents a series of instructions with no branches. -<JoshTriplett> Straight-line code. -<JoshTriplett> A branch only occurs at the end of a basic block, and branches can only target the beginning of a basic block. -<JoshTriplett> Typically, a conditional will consist of a basic block leading up to the branch, a basic block for the true case, a basic block for the false case, and a basic block where the two paths merge back together. -<JoshTriplett> Either the true or the false case may not exist. -<JoshTriplett> A loop will normally have a basic block for the loop body, which can branch to the top at the end or continue to the next basic block. -<JoshTriplett> So basic blocks represent a node in the control flow graph. -<JoshTriplett> The edges in that graph lead from one basic block to a basic block which can follow it in the execution of the program. -<JoshTriplett> Each basic block has a series of instructions, "struct instruction". -<JoshTriplett> "enum opcode" lists all the instructions. -<JoshTriplett> Fairly high-level instruction set, corresponding directly to bits of C. -<JoshTriplett> So you have an entrypoint, which has a graph of basic blocks, each of which has a list of instructions. -<JoshTriplett> An entrypoint also has a pointer to the first instruction. -<JoshTriplett> One last bit of trickiness: struct pseudo. -<JoshTriplett> Have you ever heard of "static single assignment" or SSA form? -<JoshTriplett> struct pseudo represents one of those single-assignment variables. -<JoshTriplett> Each one has a pointer to the symbol it represents (which may have many pseudos referencing it). -<JoshTriplett> Each one also has a pointer to the instruction that defines it. -<JoshTriplett> That covers most of the major data structures in Sparse. -<JoshTriplett> Now, given all that, some of the top-level stuff in sparse.c may make more sense. -<JoshTriplett> For instance, the context checking works in terms of basic blocks. -<JoshTriplett> Hopefully some of that helped you understand Sparse better. - +As far as the parsing structures go... +The C parser exists in two main files: parse.c, which parses statements, and expression.c, which parses expressions. +parse.h contains the definition of struct statement, which represents a C statement. +That includes only those things which can't appear as an expression, which primarily includes control flow statements such as if, loops, switch/case, and goto. +expression.h contains the definition of struct expression, which represents a C expression. That has a lot more content, since most C constructs can appear in expressions. +A series of statements forms a compound statement (STMT_COMPOUND). +That appears as another struct statement which has a statement_list member. +A function body consists of a compound statement. +When you look at a loop body, if or else body, or case body, you'll notice that they just have a struct statement, not a statement_list; they can have multiple statements by using a compound statement. +Also note that all loops get turned into a single "iterator" statement. +for, while, and do-while. +A symbol, then, represents a name in a C file. A symbol might represent a variable, a function, a label, or various other things. +See symbol.h. +"struct symbol" represents one symbol. +As with the various other structures, it has some common data and a union of sub-structures for the parts that differ between different types. +Most of the interesting bits come in the NS_SYMBOL case. +Among other things, it has a struct statement for the body of a function (if any), a list of symbols for the arguments, an expression for a variable initializer, and so on. +Together, struct symbol, struct statement, and struct expression represent most of the abstract syntax tree for C. +So, that represents most of the "front-end" of Sparse: parsing C and generating that abstract syntax tree. +That much occurs in pretty much any program using the Sparse frontend. +The backend varies among programs. +For instance, the c2xml backend goes that far, then outputs XML. +The sparse static analysis backend has a few steps: it generates linearized bytecode, does some evaluation on that, and outputs some warnings. +Several other backends run that linearized bytecode stage. +The linearized bytecode itself has a set of nested structures. +linearize.h defines all of them. +At the top level, it has struct entrypoint. +That represents an entrypoint to the code, which would normally mean a function. +An entrypoint has a list of basic blocks. +struct basic_block. +A basic block represents a series of instructions with no branches. +Straight-line code. +A branch only occurs at the end of a basic block, and branches can only target the beginning of a basic block. +Typically, a conditional will consist of a basic block leading up to the branch, a basic block for the true case, a basic block for the false case, and a basic block where the two paths merge back together. +Either the true or the false case may not exist. +A loop will normally have a basic block for the loop body, which can branch to the top at the end or continue to the next basic block. +So basic blocks represent a node in the control flow graph. +The edges in that graph lead from one basic block to a basic block which can follow it in the execution of the program. +Each basic block has a series of instructions, "struct instruction". +"enum opcode" lists all the instructions. +Fairly high-level instruction set, corresponding directly to bits of C. +So you have an entrypoint, which has a graph of basic blocks, each of which has a list of instructions. +An entrypoint also has a pointer to the first instruction. +One last bit of trickiness: struct pseudo. +Have you ever heard of "static single assignment" or SSA form? +struct pseudo represents one of those single-assignment variables. +Each one has a pointer to the symbol it represents (which may have many pseudos referencing it). +Each one also has a pointer to the instruction that defines it. +That covers most of the major data structures in Sparse. +Now, given all that, some of the top-level stuff in sparse.c may make more sense. +For instance, the context checking works in terms of basic blocks. +Hopefully some of that helped you understand Sparse better. diff --git a/Documentation/test-suite b/Documentation/test-suite index 160fed31..1315dbd6 100644 --- a/Documentation/test-suite +++ b/Documentation/test-suite @@ -40,7 +40,7 @@ check-output-start / check-output-end check-output-ignore / check-error-ignore Don't check the expected output (stdout or stderr) of check-command - (usefull when this output is not comparable or if you're only interested + (useful when this output is not comparable or if you're only interested in the exit value). By default this check is done. @@ -60,8 +60,8 @@ check-output-excludes: <pattern> check-output-pattern(<nbr>): <pattern> check-output-pattern(<min>,<max>): <pattern> - Similar to the '-contains/excludes' here above, but with full control - of the number of times the pattern should occurs in the output. + Similar to the contains/excludes above, but with full control + of the number of times the pattern should occur in the output. If <min> or <max> is '-' the corresponding check is ignored. Using test-suite @@ -91,7 +91,7 @@ cmd: "sparse $file". The output of the test-suite format command can be redirected into the -test case to create a test-suite formated file. +test case to create a test-suite formatted file. $ ./test-suite format bad-assignment.c Assignment >> bad-assignment.c $ cat !$ @@ -47,6 +47,17 @@ struct symbol *current_fn; static struct symbol *degenerate(struct expression *expr); static struct symbol *evaluate_symbol(struct symbol *sym); +static inline int valid_expr_type(struct expression *expr) +{ + return expr && valid_type(expr->ctype); +} + +static inline int valid_subexpr_type(struct expression *expr) +{ + return valid_expr_type(expr->left) + && valid_expr_type(expr->right); +} + static struct symbol *evaluate_symbol_expression(struct expression *expr) { struct expression *addr; @@ -393,15 +404,20 @@ static inline int is_string_type(struct symbol *type) static struct symbol *bad_expr_type(struct expression *expr) { - sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); switch (expr->type) { case EXPR_BINOP: case EXPR_COMPARE: + if (!valid_subexpr_type(expr)) + break; + sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); info(expr->pos, " left side has type %s", show_typename(expr->left->ctype)); info(expr->pos, " right side has type %s", show_typename(expr->right->ctype)); break; case EXPR_PREOP: case EXPR_POSTOP: + if (!valid_expr_type(expr->unop)) + break; + sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op)); info(expr->pos, " argument has type %s", show_typename(expr->unop->ctype)); break; default: @@ -847,8 +863,10 @@ static struct symbol *evaluate_ptr_sub(struct expression *expr) val->value = value; if (value & (value-1)) { - if (Wptr_subtraction_blows) + if (Wptr_subtraction_blows) { warning(expr->pos, "potentially expensive pointer subtraction"); + info(expr->pos, " '%s' has a non-power-of-2 size: %lu", show_typename(lbase), value); + } } sub->op = '-'; @@ -877,23 +895,23 @@ static struct symbol *evaluate_conditional(struct expression *expr, int iterator warning(expr->pos, "assignment expression in conditional"); ctype = evaluate_expression(expr); - if (ctype) { - if (is_safe_type(ctype)) - warning(expr->pos, "testing a 'safe expression'"); - if (is_func_type(ctype)) { - if (Waddress) - warning(expr->pos, "the address of %s will always evaluate as true", "a function"); - } else if (is_array_type(ctype)) { - if (Waddress) - warning(expr->pos, "the address of %s will always evaluate as true", "an array"); - } else if (!is_scalar_type(ctype)) { - sparse_error(expr->pos, "incorrect type in conditional"); - info(expr->pos, " got %s", show_typename(ctype)); - ctype = NULL; - } + if (!valid_type(ctype)) + return NULL; + if (is_safe_type(ctype)) + warning(expr->pos, "testing a 'safe expression'"); + if (is_func_type(ctype)) { + if (Waddress) + warning(expr->pos, "the address of %s will always evaluate as true", "a function"); + } else if (is_array_type(ctype)) { + if (Waddress) + warning(expr->pos, "the address of %s will always evaluate as true", "an array"); + } else if (!is_scalar_type(ctype)) { + sparse_error(expr->pos, "incorrect type in conditional"); + info(expr->pos, " got %s", show_typename(ctype)); + return NULL; } - ctype = degenerate(expr); + ctype = degenerate(expr); return ctype; } @@ -3187,9 +3205,9 @@ struct symbol *evaluate_expression(struct expression *expr) case EXPR_SYMBOL: return evaluate_symbol_expression(expr); case EXPR_BINOP: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_binop(expr); case EXPR_LOGICAL: @@ -3200,15 +3218,15 @@ struct symbol *evaluate_expression(struct expression *expr) return NULL; return evaluate_comma(expr); case EXPR_COMPARE: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_compare(expr); case EXPR_ASSIGNMENT: - if (!evaluate_expression(expr->left)) - return NULL; - if (!evaluate_expression(expr->right)) + evaluate_expression(expr->left); + evaluate_expression(expr->right); + if (!valid_subexpr_type(expr)) return NULL; return evaluate_assignment(expr); case EXPR_PREOP: @@ -682,6 +682,7 @@ static char **handle_onoff_switch(char *arg, char **next, const struct flag warn if (*warnings[i].flag != WARNING_FORCE_OFF && warnings[i].flag != &Wsparse_error) *warnings[i].flag = WARNING_ON; } + return NULL; } // Prefixes "no" and "no-" mean to turn warning off. diff --git a/linearize.c b/linearize.c index b1a1dd4b..3b4b0d9b 100644 --- a/linearize.c +++ b/linearize.c @@ -1277,10 +1277,8 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi struct symbol *fntype; struct context *context; - if (!expr->ctype) { - warning(expr->pos, "call with no type!"); + if (!expr->ctype) return VOID; - } fn = expr->fn; fntype = fn->ctype; @@ -393,6 +393,131 @@ static unsigned int operand_size(struct instruction *insn, pseudo_t pseudo) return size; } +static pseudo_t eval_insn(struct instruction *insn) +{ + /* FIXME! Verify signs and sizes!! */ + unsigned int size = insn->size; + long long left = insn->src1->value; + long long right = insn->src2->value; + unsigned long long ul, ur; + long long res, mask, bits; + + mask = 1ULL << (size-1); + bits = mask | (mask-1); + + if (left & mask) + left |= ~bits; + if (right & mask) + right |= ~bits; + ul = left & bits; + ur = right & bits; + + switch (insn->opcode) { + case OP_ADD: + res = left + right; + break; + case OP_SUB: + res = left - right; + break; + case OP_MULU: + res = ul * ur; + break; + case OP_MULS: + res = left * right; + break; + case OP_DIVU: + if (!ur) + goto undef; + res = ul / ur; + break; + case OP_DIVS: + if (!right) + goto undef; + if (left == mask && right == -1) + goto undef; + res = left / right; + break; + case OP_MODU: + if (!ur) + goto undef; + res = ul % ur; + break; + case OP_MODS: + if (!right) + goto undef; + if (left == mask && right == -1) + goto undef; + res = left % right; + break; + case OP_SHL: + res = left << right; + break; + case OP_LSR: + res = ul >> ur; + break; + case OP_ASR: + res = left >> right; + break; + /* Logical */ + case OP_AND: + res = left & right; + break; + case OP_OR: + res = left | right; + break; + case OP_XOR: + res = left ^ right; + break; + case OP_AND_BOOL: + res = left && right; + break; + case OP_OR_BOOL: + res = left || right; + break; + + /* Binary comparison */ + case OP_SET_EQ: + res = left == right; + break; + case OP_SET_NE: + res = left != right; + break; + case OP_SET_LE: + res = left <= right; + break; + case OP_SET_GE: + res = left >= right; + break; + case OP_SET_LT: + res = left < right; + break; + case OP_SET_GT: + res = left > right; + break; + case OP_SET_B: + res = ul < ur; + break; + case OP_SET_A: + res = ul > ur; + break; + case OP_SET_BE: + res = ul <= ur; + break; + case OP_SET_AE: + res = ul >= ur; + break; + default: + return NULL; + } + res &= bits; + + return value_pseudo(res); + +undef: + return NULL; +} + + static int simplify_asr(struct instruction *insn, pseudo_t pseudo, long long value) { unsigned int size = operand_size(insn, pseudo); @@ -548,122 +673,12 @@ static int simplify_constant_leftside(struct instruction *insn) static int simplify_constant_binop(struct instruction *insn) { - /* FIXME! Verify signs and sizes!! */ - long long left = insn->src1->value; - long long right = insn->src2->value; - unsigned long long ul, ur; - long long res, mask, bits; - - mask = 1ULL << (insn->size-1); - bits = mask | (mask-1); - - if (left & mask) - left |= ~bits; - if (right & mask) - right |= ~bits; - ul = left & bits; - ur = right & bits; + pseudo_t res = eval_insn(insn); - switch (insn->opcode) { - case OP_ADD: - res = left + right; - break; - case OP_SUB: - res = left - right; - break; - case OP_MULU: - res = ul * ur; - break; - case OP_MULS: - res = left * right; - break; - case OP_DIVU: - if (!ur) - return 0; - res = ul / ur; - break; - case OP_DIVS: - if (!right) - return 0; - if (left == mask && right == -1) - return 0; - res = left / right; - break; - case OP_MODU: - if (!ur) - return 0; - res = ul % ur; - break; - case OP_MODS: - if (!right) - return 0; - if (left == mask && right == -1) - return 0; - res = left % right; - break; - case OP_SHL: - res = left << right; - break; - case OP_LSR: - res = ul >> ur; - break; - case OP_ASR: - res = left >> right; - break; - /* Logical */ - case OP_AND: - res = left & right; - break; - case OP_OR: - res = left | right; - break; - case OP_XOR: - res = left ^ right; - break; - case OP_AND_BOOL: - res = left && right; - break; - case OP_OR_BOOL: - res = left || right; - break; - - /* Binary comparison */ - case OP_SET_EQ: - res = left == right; - break; - case OP_SET_NE: - res = left != right; - break; - case OP_SET_LE: - res = left <= right; - break; - case OP_SET_GE: - res = left >= right; - break; - case OP_SET_LT: - res = left < right; - break; - case OP_SET_GT: - res = left > right; - break; - case OP_SET_B: - res = ul < ur; - break; - case OP_SET_A: - res = ul > ur; - break; - case OP_SET_BE: - res = ul <= ur; - break; - case OP_SET_AE: - res = ul >= ur; - break; - default: + if (!res) return 0; - } - res &= bits; - replace_with_pseudo(insn, value_pseudo(res)); + replace_with_pseudo(insn, res); return REPEAT_CSE; } @@ -62,7 +62,7 @@ arithmetic operations other than bitwise operations, and on any conversion of one restricted type into another, except via a cast that includes \fB__attribute__((force))\fR. -__bitwise ends up being a "stronger integer separation". That one +__bitwise ends up being a "stronger integer separation", one that doesn't allow you to mix with non-bitwise integers, so now it's much harder to lose the type by mistake. @@ -210,7 +210,7 @@ Sparse issues these warnings by default. To turn them off, use .B \-Winit\-cstring Warn about initialization of a char array with a too long constant C string. -If the size of the char array and the length of the string is the same, +If the size of the char array and the length of the string are the same, there is no space for the last nul char of the string in the array: .nf @@ -354,7 +354,7 @@ Warn about preprocessor conditionals that use the value of an undefined preprocessor symbol. Standard C (C99 6.10.1) permits using the value of an undefined preprocessor -symbol in preprocessor conditionals, and specifies it has have a value of 0. +symbol in preprocessor conditionals, and specifies it has a value of 0. However, this behavior can lead to subtle errors. Sparse does not issue these warnings by default. @@ -371,6 +371,7 @@ The \fIdir\fR name would normally take the form of the target's normalized GNU triplet. (e.g. i386-linux-gnu). . .SH DEBUG OPTIONS +.TP .B \-fmem-report Report some statistics about memory allocation used by the tool. . @@ -496,7 +496,9 @@ struct symbol *examine_symbol_type(struct symbol * sym) sym->ctype.base_type = base; return examine_node_type(sym); } - break; + sym->type = SYM_NODE; + sym->ctype.base_type = &bad_ctype; + return sym; } case SYM_PREPROCESSOR: sparse_error(sym->pos, "ctype on preprocessor command? (%s)", show_ident(sym->ident)); @@ -309,6 +309,11 @@ extern void debug_symbol(struct symbol *); extern void merge_type(struct symbol *sym, struct symbol *base_type); extern void check_declaration(struct symbol *sym); +static inline int valid_type(const struct symbol *ctype) +{ + return ctype && ctype != &bad_ctype; +} + static inline struct symbol *get_base_type(const struct symbol *sym) { return examine_symbol_type(sym->ctype.base_type); diff --git a/validation/bad-type-twice0.c b/validation/bad-type-twice0.c new file mode 100644 index 00000000..7a9073c5 --- /dev/null +++ b/validation/bad-type-twice0.c @@ -0,0 +1,13 @@ +static int foo(a) +{ + return a ? : 1; +} + +/* + * check-name: bad-type-twice0 + * + * check-error-start +bad-type-twice0.c:3:16: error: incorrect type in conditional +bad-type-twice0.c:3:16: got incomplete type a + * check-error-end + */ diff --git a/validation/bad-type-twice1.c b/validation/bad-type-twice1.c new file mode 100644 index 00000000..95cfd9e0 --- /dev/null +++ b/validation/bad-type-twice1.c @@ -0,0 +1,16 @@ +static unsigned long foo(unsigned long val, void *ref) +{ + if (val >= ref) + val = 0; + return val; +} + +/* + * check-name: bad-type-twice1 + * + * check-error-start +bad-type-twice1.c:3:17: error: incompatible types for operation (>=) +bad-type-twice1.c:3:17: left side has type unsigned long [unsigned] val +bad-type-twice1.c:3:17: right side has type void *ref + * check-error-end + */ diff --git a/validation/bad-type-twice2.c b/validation/bad-type-twice2.c new file mode 100644 index 00000000..0aadd7a3 --- /dev/null +++ b/validation/bad-type-twice2.c @@ -0,0 +1,18 @@ +extern type_t fun(int); + +int foo(int x, int y) +{ + return ((int)fun(y)) + x; +} + +/* + * check-name: bad-type-twice2 + * + * check-error-start +bad-type-twice2.c:1:8: warning: 'type_t' has implicit type +bad-type-twice2.c:1:15: error: Expected ; at end of declaration +bad-type-twice2.c:1:15: error: got fun +bad-type-twice2.c:5:22: error: undefined identifier 'fun' +bad-type-twice2.c:5:18: error: cast from unknown type + * check-error-end + */ diff --git a/validation/ptr-sub-blows.c b/validation/ptr-sub-blows.c new file mode 100644 index 00000000..af3d79e7 --- /dev/null +++ b/validation/ptr-sub-blows.c @@ -0,0 +1,23 @@ +static int ok(int *a, int *b) +{ + return a - b; +} + +struct s { + int a, b, c; +}; + +static int ko(struct s *a, struct s *b) +{ + return a - b; +} + +/* + * check-name: ptr-sub-blows + * check-command: sparse -Wptr-subtraction-blows $file + * + * check-error-start +ptr-sub-blows.c:12:18: warning: potentially expensive pointer subtraction +ptr-sub-blows.c:12:18: 'struct s' has a non-power-of-2 size: 12 + * check-error-end + */ diff --git a/validation/typeof-bad.c b/validation/typeof-bad.c new file mode 100644 index 00000000..a9366bad --- /dev/null +++ b/validation/typeof-bad.c @@ -0,0 +1,17 @@ +static typeof(undef) a; + +static int foo(void) +{ + return a; +} + +/* + * check-name: typeof-bad + * + * check-error-start +typeof-bad.c:1:15: error: undefined identifier 'undef' +typeof-bad.c:5:16: warning: incorrect type in return expression (different base types) +typeof-bad.c:5:16: expected int +typeof-bad.c:5:16: got bad type static [toplevel] a + * check-error-end + */ |
