blob: 85cece72b3440c30db0ad952fce401ce4ca53e04 [file] [log] [blame]
/* { dg-additional-options "-fanalyzer-call-summaries --param analyzer-min-snodes-for-call-summary=0" } */
/* There need to be at least two calls to a function for the
call-summarization code to be used.
TODO: add some kind of test that summarization *was* used. */
#include <stdlib.h>
#include "analyzer-decls.h"
extern int external_fn (void *);
int returns_const (void)
{
return 42;
}
void test_summarized_returns_const (void)
{
__analyzer_eval (returns_const () == 42); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_const () == 42); /* { dg-warning "TRUE" } */
}
void test_summarized_returns_const_2 (void)
{
returns_const (); /* { dg-message "when 'returns_const' returns" } */
__analyzer_dump_path (); /* { dg-message "path" } */
}
int returns_param (int i)
{
return i;
}
void test_summarized_returns_param (int j)
{
__analyzer_eval (returns_param (j) == j); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_param (j) == j); /* { dg-warning "TRUE" } */
}
void writes_const_to_ptr (int *p)
{
*p = 42;
}
void test_summarized_writes_const_to_ptr (void)
{
int i, j;
writes_const_to_ptr (&i);
writes_const_to_ptr (&j);
__analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
__analyzer_eval (j == 42); /* { dg-warning "TRUE" } */
}
// TODO: we should complain about this:
void test_summarized_write_through_null (void)
{
writes_const_to_ptr (NULL);
}
void writes_param_to_ptr (int i, int *p)
{
*p = i;
}
void test_summarized_writes_param_to_ptr (int j)
{
int x, y;
writes_param_to_ptr (j, &x);
writes_param_to_ptr (j, &y);
__analyzer_eval (x == j); /* { dg-warning "TRUE" } */
__analyzer_eval (y == j); /* { dg-warning "TRUE" } */
}
void test_summarized_writes_param_to_ptr_unknown (int j)
{
int *p = (int *)__analyzer_get_unknown_ptr ();
writes_param_to_ptr (j, p);
__analyzer_eval (*p == j); /* { dg-warning "UNKNOWN" } */
}
int g;
void writes_to_global (int i)
{
g = i;
}
void test_writes_to_global (int x, int y)
{
writes_to_global (x);
__analyzer_eval (g == x); /* { dg-warning "TRUE" } */
writes_to_global (y);
__analyzer_eval (g == y); /* { dg-warning "TRUE" } */
}
int reads_from_global (void)
{
return g;
}
void test_reads_from_global (int x, int y)
{
g = x;
__analyzer_eval (reads_from_global () == x); /* { dg-warning "TRUE" } */
g = y;
__analyzer_eval (reads_from_global () == y); /* { dg-warning "TRUE" } */
}
/* Example of a unary op. */
int returns_negation (int i)
{
return -i;
}
void test_returns_negation (int x)
{
__analyzer_eval (returns_negation (5) == -5); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_negation (x) == -x); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_negation (-x) == x); /* { dg-warning "TRUE" } */
}
/* Example of a binary op. */
int returns_sum (int i, int j)
{
return i + j;
}
void test_returns_sum (int x, int y)
{
__analyzer_eval (returns_sum (5, 3) == 8); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_sum (7, 2) == 9); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_sum (x, y) == x + y); /* { dg-warning "TRUE" } */
}
struct coord
{
int x;
int y;
};
struct coord make_coord (int x, int y)
{
struct coord result = {x, y};
return result;
}
void test_make_coord (int i, int j)
{
struct coord c1 = make_coord (3, 4);
__analyzer_eval (c1.x == 3); /* { dg-warning "TRUE" } */
__analyzer_eval (c1.y == 4); /* { dg-warning "TRUE" } */
struct coord c2 = make_coord (i, j);
__analyzer_eval (c2.x == i); /* { dg-warning "TRUE" } */
__analyzer_eval (c2.y == j); /* { dg-warning "TRUE" } */
}
/* Example of nested usage of summaries. */
struct rect
{
struct coord nw;
struct coord se;
};
struct rect make_rect (int top, int bottom, int left, int right)
{
struct rect result = {make_coord (left, top),
make_coord (right, bottom)};
return result;
}
void test_make_rect (int top, int bottom, int left, int right)
{
struct rect r1 = make_rect (3, 4, 5, 6);
__analyzer_eval (r1.nw.y == 3); /* { dg-warning "TRUE" } */
__analyzer_eval (r1.se.y == 4); /* { dg-warning "TRUE" } */
__analyzer_eval (r1.nw.x == 5); /* { dg-warning "TRUE" } */
__analyzer_eval (r1.se.x == 6); /* { dg-warning "TRUE" } */
struct rect r2 = make_rect (top, bottom, left, right);
__analyzer_eval (r2.nw.y == top); /* { dg-warning "TRUE" } */
__analyzer_eval (r2.se.y == bottom); /* { dg-warning "TRUE" } */
__analyzer_eval (r2.nw.x == left); /* { dg-warning "TRUE" } */
__analyzer_eval (r2.se.x == right); /* { dg-warning "TRUE" } */
}
const char *returns_str (void)
{
return "abc";
}
void test_returns_str (void)
{
__analyzer_eval (returns_str () != NULL); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_str ()[0] == 'a'); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_str ()[1] == 'b'); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_str ()[2] == 'c'); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_str ()[3] == '\0'); /* { dg-warning "TRUE" } */
}
int returns_field (struct coord *p)
{
return p->y;
}
void test_returns_field (struct coord *q)
{
__analyzer_eval (returns_field (q) == q->y); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_field (q) == q->y); /* { dg-warning "TRUE" } */
}
void writes_to_fields (struct coord *p, int x, int y)
{
p->x = x;
p->y = y;
}
void test_writes_to_field (struct coord *q, int qx, int qy)
{
struct coord a, b;
writes_to_fields (&a, 1, 2);
__analyzer_eval (a.x == 1); /* { dg-warning "TRUE" } */
__analyzer_eval (a.y == 2); /* { dg-warning "TRUE" } */
writes_to_fields (&b, 3, 4);
__analyzer_eval (b.x == 3); /* { dg-warning "TRUE" } */
__analyzer_eval (b.y == 4); /* { dg-warning "TRUE" } */
writes_to_fields (q, qx, qy);
__analyzer_eval (q->x == qx); /* { dg-warning "TRUE" } */
__analyzer_eval (q->y == qy); /* { dg-warning "TRUE" } */
}
/* Example of nested function summarization. */
int get_min_x (struct rect *p)
{
return p->nw.x;
}
int get_min_y (struct rect *p)
{
return p->nw.y;
}
int get_max_x (struct rect *p)
{
return p->se.x;
}
int get_max_y (struct rect *p)
{
return p->se.y;
}
int get_area (struct rect *p)
{
return ((get_max_x (p) - get_min_x (p))
* (get_max_y (p) - get_min_y (p)));
}
void test_get_area (int top, int bottom, int left, int right, struct rect *p)
{
{
/* 1x1 at origin. */
struct rect a = make_rect (0, 1, 0, 1);
__analyzer_eval (get_min_y (&a) == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_y (&a) == 1); /* { dg-warning "TRUE" } */
__analyzer_eval (get_min_x (&a) == 0); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_x (&a) == 1); /* { dg-warning "TRUE" } */
__analyzer_eval (get_area (&a) == 1); /* { dg-warning "TRUE" } */
}
{
/* 4x5. */
struct rect b = make_rect (3, 7, 4, 9);
__analyzer_eval (get_min_y (&b) == 3); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_y (&b) == 7); /* { dg-warning "TRUE" } */
__analyzer_eval (get_min_x (&b) == 4); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_x (&b) == 9); /* { dg-warning "TRUE" } */
__analyzer_eval (get_area (&b) == 20); /* { dg-warning "TRUE" } */
}
{
/* Symbolic. */
struct rect c = make_rect (top, bottom, left, right);
__analyzer_eval (get_min_y (&c) == top); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_y (&c) == bottom); /* { dg-warning "TRUE" } */
__analyzer_eval (get_min_x (&c) == left); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_x (&c) == right); /* { dg-warning "TRUE" } */
__analyzer_eval (get_area (&c) == ((right - left) * (bottom - top))); /* { dg-warning "TRUE" } */
}
/* Symbolic via ptr. */
__analyzer_eval (get_min_y (p) == p->nw.y); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_y (p) == p->se.y); /* { dg-warning "TRUE" } */
__analyzer_eval (get_min_x (p) == p->nw.x); /* { dg-warning "TRUE" } */
__analyzer_eval (get_max_x (p) == p->se.x); /* { dg-warning "TRUE" } */
__analyzer_eval (get_area (p) == ((p->se.x - p->nw.x) * (p->se.y - p->nw.y))); /* { dg-warning "TRUE" } */
}
int returns_element (int i)
{
static const int arr[3] = {7, 8, 9};
return arr[i];
}
void test_returns_element (int j)
{
__analyzer_eval (returns_element (0) == 7); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_element (1) == 8); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_element (2) == 9); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_element (3) == 10); /* { dg-warning "UNKNOWN" } */
// TODO: out of bounds
}
const int *returns_element_ptr (int i)
{
static const int arr[3] = {7, 8, 9};
return &arr[i];
}
int test_returns_element_ptr (int j)
{
__analyzer_eval (*returns_element_ptr (0) == 7); /* { dg-warning "TRUE" } */
__analyzer_eval (*returns_element_ptr (1) == 8); /* { dg-warning "TRUE" } */
__analyzer_eval (*returns_element_ptr (2) == 9); /* { dg-warning "TRUE" } */
return *returns_element_ptr (3); /* { dg-warning "buffer overread" } */
}
int returns_offset (int arr[3], int i)
{
return arr[i];
}
void test_returns_offset (int outer_arr[3], int idx)
{
int a[3] = {4, 5, 6};
__analyzer_eval (returns_offset (a, 0) == 4); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset (a, 1) == 5); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset (a, 2) == 6); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset (a, idx) == a[idx]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_offset (outer_arr, 0) == outer_arr[0]); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset (outer_arr, idx) == outer_arr[idx]); /* { dg-warning "TRUE" } */
}
int returns_offset_2 (int arr[], int i)
{
return arr[i];
}
void test_returns_offset_2 (int outer_arr[], int idx)
{
int a[3] = {4, 5, 6};
__analyzer_eval (returns_offset_2 (a, 0) == 4); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset_2 (a, 1) == 5); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset_2 (a, 2) == 6); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset_2 (a, idx) == a[idx]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_offset_2 (outer_arr, 0) == outer_arr[0]); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset_2 (outer_arr, idx) == outer_arr[idx]); /* { dg-warning "TRUE" } */
}
int returns_offset_3 (int *p, int i)
{
return p[i];
}
void test_returns_offset_3 (int *q, int j)
{
__analyzer_eval (returns_offset_3 (q, j) == q[j]); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_offset_3 (q, j) == q[j]); /* { dg-warning "TRUE" } */
}
/* With state merging, this is summarized as returning "UNKNOWN". */
int two_outcomes (int flag, int x, int y)
{
if (flag)
return x;
else
return y;
}
void test_two_outcomes (int outer_flag, int a, int b)
{
int r;
__analyzer_eval (two_outcomes (1, a, b) == a); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (two_outcomes (0, a, b) == b); /* { dg-warning "UNKNOWN" } */
r = two_outcomes (outer_flag, a, b);
if (outer_flag)
__analyzer_eval (r == a); /* { dg-warning "UNKNOWN" } */
else
__analyzer_eval (r == b); /* { dg-warning "UNKNOWN" } */
}
/* Verify that summary replays capture postconditions. */
void check_int_nonnegative (int i)
{
if (i < 0)
__builtin_unreachable ();
}
void test_check_int_nonnegative (int i, int j)
{
__analyzer_eval (i >= 0); /* { dg-warning "UNKNOWN" } */
check_int_nonnegative (i);
__analyzer_eval (i >= 0); /* { dg-warning "TRUE" } */
__analyzer_eval (j >= 0); /* { dg-warning "UNKNOWN" } */
check_int_nonnegative (j);
__analyzer_eval (j >= 0); /* { dg-warning "TRUE" } */
}
void calls_external_fn (void)
{
external_fn (NULL);
}
void test_calls_external_fn (void)
{
g = 1;
__analyzer_eval (g == 1); /* { dg-warning "TRUE" } */
calls_external_fn ();
calls_external_fn ();
__analyzer_eval (g == 1); /* { dg-warning "UNKNOWN" "expected" { xfail *-*-* } } */
/* { dg-bogus "TRUE" "bogus" { xfail *-*-* } .-1 } */
// TODO(xfails)
}
int returns_iterator (int n)
{
int i;
for (i = 0; i < n; i++)
{
}
return i;
}
void test_returns_iterator (int j, int k)
{
__analyzer_eval (returns_iterator (j) == j); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_iterator (k) == k); /* { dg-warning "UNKNOWN" } */
/* TODO: ideally we'd capture these equalities, but this is an issue
with how we handle loops. */
}
int returns_external_result (void)
{
return external_fn (NULL);
}
int test_returns_external_result (void)
{
int i, j;
i = returns_external_result ();
j = returns_external_result ();
__analyzer_eval (i == j); /* { dg-warning "UNKNOWN" } */
return i * j;
}
int uses_alloca (int i)
{
int *p = alloca (sizeof (int));
*p = i;
return *p;
}
void test_uses_alloca (int x)
{
__analyzer_eval (uses_alloca (42) == 42); /* { dg-warning "TRUE" } */
__analyzer_eval (uses_alloca (x) == x); /* { dg-warning "TRUE" } */
}
struct bits
{
unsigned b0 : 1;
unsigned b1 : 1;
unsigned b2 : 1;
unsigned b3 : 1;
unsigned b4 : 1;
unsigned b5 : 1;
unsigned b6 : 1;
unsigned b7 : 1;
};
int returns_bitfield (struct bits b)
{
return b.b3;
}
void test_returns_bitfield (struct bits s)
{
__analyzer_eval (returns_bitfield (s) == s.b3); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_bitfield (s) == s.b3); /* { dg-warning "UNKNOWN" } */
// TODO: ideally it would figure out that these are equal
}
int consume_two_ints_from_va_list (__builtin_va_list ap)
{
int i, j;
i = __builtin_va_arg (ap, int);
j = __builtin_va_arg (ap, int);
return i * j;
}
int test_consume_two_ints_from_va_list (__builtin_va_list ap1)
{
int p1, p2;
__builtin_va_list ap2;
__builtin_va_copy (ap2, ap1);
p1 = consume_two_ints_from_va_list (ap1);
p2 = consume_two_ints_from_va_list (ap2);
__analyzer_eval (p1 == p2); /* { dg-warning "UNKNOWN" } */
// TODO: ideally it would figure out these are equal
__builtin_va_end (ap2);
}
int consume_two_ints_from_varargs (int placeholder, ...)
{
int i, j;
__builtin_va_list ap;
__builtin_va_start (ap, placeholder);
i = __builtin_va_arg (ap, int);
j = __builtin_va_arg (ap, int);
__builtin_va_end (ap);
return i * j;
}
void test_consume_two_ints_from_varargs (int x, int y)
{
__analyzer_eval (consume_two_ints_from_varargs (0, 4, 5) == 20); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (consume_two_ints_from_varargs (0, 3, 6) == 18); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (consume_two_ints_from_varargs (0, x, 6) == x * y); /* { dg-warning "UNKNOWN" } */
// TODO: ideally it would figure these out
}
extern int const_fn_1 (int) __attribute__ ((const));
int calls_const_fn (int i)
{
return const_fn_1 (i);
}
void test_calls_const_fn (int x)
{
int r1, r2;
r1 = calls_const_fn (x);
r2 = calls_const_fn (x);
__analyzer_eval (r1 == r2); /* { dg-warning "TRUE" } */
}
typedef struct iv2 { int arr[2]; } iv2_t;
typedef struct iv4 { int arr[4]; } iv4_t;
iv2_t returns_iv2_t (int x, int y)
{
iv2_t result = {x, y};
return result;
}
void test_returns_iv2_t (int a, int b)
{
__analyzer_eval (returns_iv2_t (a, b).arr[0] == a); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_iv2_t (a, b).arr[1] == b); /* { dg-warning "TRUE" } */
}
iv4_t returns_iv4_t (int a, iv2_t bc, int d)
{
iv4_t result = {a, bc.arr[0], bc.arr[1], d};
return result;
}
void test_returns_iv4_t (int p, iv2_t qr, int s)
{
__analyzer_eval (returns_iv4_t (p, qr, s).arr[0] == p); /* { dg-warning "TRUE" } */
__analyzer_eval (returns_iv4_t (p, qr, s).arr[1] == qr.arr[0]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_iv4_t (p, qr, s).arr[2] == qr.arr[1]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_iv4_t (p, qr, s).arr[3] == s); /* { dg-warning "TRUE" } */
// TODO: ideally the UNKNOWNs would be TRUEs.
}
void copies_iv2_t (int *p, iv2_t xy)
{
__builtin_memcpy (p, &xy, sizeof (xy));
}
void test_copies_iv2_t (iv2_t ab, iv2_t cd)
{
iv4_t t;
copies_iv2_t (&t.arr[0], ab);
copies_iv2_t (&t.arr[2], cd);
__analyzer_eval (t.arr[0] = ab.arr[0]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (t.arr[1] = ab.arr[1]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (t.arr[2] = cd.arr[0]); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (t.arr[3] = cd.arr[1]); /* { dg-warning "UNKNOWN" } */
// TODO: ideally the UNKNOWNs would be TRUEs.
}
void partially_inits (int *p, int v)
{
p[1] = v;
}
void test_partially_inits (int x)
{
int arr[2];
partially_inits (arr, x);
partially_inits (arr, x);
__analyzer_eval (arr[0]); /* { dg-warning "UNKNOWN" "eval" } */
/* { dg-warning "use of uninitialized value 'arr\\\[0\\\]'" "uninit" { target *-*-* } .-1 } */
__analyzer_eval (arr[1] == x); /* { dg-warning "UNKNOWN" "eval" } */
/* { dg-bogus "use of uninitialized value 'arr\\\[1\\\]'" "uninit" { xfail *-*-* } .-1 } */
// TODO(xfail), and eval should be "TRUE"
}
int uses_switch (int i)
{
switch (i)
{
case 0:
return 42;
case 1:
return 17;
default:
return i * 2;
}
}
void test_uses_switch (int x)
{
__analyzer_eval (uses_switch (0) == 42); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (uses_switch (1) == 17); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (uses_switch (2) == x * 2); /* { dg-warning "UNKNOWN" } */
// TODO: ideally the UNKNOWNs would be TRUEs.
}
int *returns_ptr_to_first_field (struct coord *p)
{
return &p->x;
}
void test_returns_ptr_to_first_field (struct coord *q)
{
__analyzer_eval (returns_ptr_to_first_field (q) == (int *)q); /* { dg-warning "UNKNOWN" } */
__analyzer_eval (returns_ptr_to_first_field (q) == (int *)q); /* { dg-warning "UNKNOWN" } */
// TODO: ideally the UNKNOWNs would be TRUEs.
}