summaryrefslogtreecommitdiff
path: root/clang/test/Analysis/idempotent-operations.c
diff options
context:
space:
mode:
Diffstat (limited to 'clang/test/Analysis/idempotent-operations.c')
-rw-r--r--clang/test/Analysis/idempotent-operations.c236
1 files changed, 236 insertions, 0 deletions
diff --git a/clang/test/Analysis/idempotent-operations.c b/clang/test/Analysis/idempotent-operations.c
new file mode 100644
index 0000000..6cc9a01
--- /dev/null
+++ b/clang/test/Analysis/idempotent-operations.c
@@ -0,0 +1,236 @@
+// RUN: %clang_cc1 -analyze -analyzer-store=region -analyzer-constraints=range -fblocks -analyzer-opt-analyze-nested-blocks -analyzer-checker=experimental.deadcode.IdempotentOperations -verify %s
+
+// Basic tests
+
+extern void test(int i);
+extern void test_f(float f);
+
+unsigned basic() {
+ int x = 10, zero = 0, one = 1;
+
+ // x op x
+ x = x; // expected-warning {{Assigned value is always the same as the existing value}}
+ test(x - x); // expected-warning {{Both operands to '-' always have the same value}}
+ x -= x; // expected-warning {{Both operands to '-=' always have the same value}}
+ x = 10; // no-warning
+ test(x / x); // expected-warning {{Both operands to '/' always have the same value}}
+ x /= x; // expected-warning {{Both operands to '/=' always have the same value}}
+ x = 10; // no-warning
+ test(x & x); // expected-warning {{Both operands to '&' always have the same value}}
+ x &= x; // expected-warning {{Both operands to '&=' always have the same value}}
+ test(x | x); // expected-warning {{Both operands to '|' always have the same value}}
+ x |= x; // expected-warning {{Both operands to '|=' always have the same value}}
+
+ // x op 1
+ test(x * one); // expected-warning {{The right operand to '*' is always 1}}
+ x *= one; // expected-warning {{The right operand to '*=' is always 1}}
+ test(x / one); // expected-warning {{The right operand to '/' is always 1}}
+ x /= one; // expected-warning {{The right operand to '/=' is always 1}}
+
+ // 1 op x
+ test(one * x); // expected-warning {{The left operand to '*' is always 1}}
+
+ // x op 0
+ test(x + zero); // expected-warning {{The right operand to '+' is always 0}}
+ test(x - zero); // expected-warning {{The right operand to '-' is always 0}}
+ test(x * zero); // expected-warning {{The right operand to '*' is always 0}}
+ test(x & zero); // expected-warning {{The right operand to '&' is always 0}}
+ test(x | zero); // expected-warning {{The right operand to '|' is always 0}}
+ test(x ^ zero); // expected-warning {{The right operand to '^' is always 0}}
+ test(x << zero); // expected-warning {{The right operand to '<<' is always 0}}
+ test(x >> zero); // expected-warning {{The right operand to '>>' is always 0}}
+
+ // 0 op x
+ test(zero + x); // expected-warning {{The left operand to '+' is always 0}}
+ test(zero - x); // expected-warning {{The left operand to '-' is always 0}}
+ test(zero / x); // expected-warning {{The left operand to '/' is always 0}}
+ test(zero * x); // expected-warning {{The left operand to '*' is always 0}}
+ test(zero & x); // expected-warning {{The left operand to '&' is always 0}}
+ test(zero | x); // expected-warning {{The left operand to '|' is always 0}}
+ test(zero ^ x); // expected-warning {{The left operand to '^' is always 0}}
+ test(zero << x); // expected-warning {{The left operand to '<<' is always 0}}
+ test(zero >> x); // expected-warning {{The left operand to '>>' is always 0}}
+
+ // Overwrite the values so these aren't marked as Pseudoconstants
+ x = 1;
+ zero = 2;
+ one = 3;
+
+ return x + zero + one;
+}
+
+void floats(float x) {
+ test_f(x * 1.0); // no-warning
+ test_f(x * 1.0F); // no-warning
+}
+
+// Ensure that we don't report false poitives in complex loops
+void bailout() {
+ int unused = 0, result = 4;
+ result = result; // expected-warning {{Assigned value is always the same as the existing value}}
+
+ for (unsigned bg = 0; bg < 1024; bg ++) {
+ result = bg * result; // no-warning
+
+ for (int i = 0; i < 256; i++) {
+ unused *= i; // no-warning
+ }
+ }
+}
+
+// Relaxed liveness - check that we don't kill liveness at assignments
+typedef unsigned uintptr_t;
+void kill_at_assign() {
+ short array[2];
+ uintptr_t x = (uintptr_t) array;
+ short *p = (short *) x;
+
+ // The following branch should be infeasible.
+ if (!(p = &array[0])) { // expected-warning{{Assigned value is always the same as the existing value}}
+ p = 0;
+ *p = 1; // no-warning
+ }
+}
+
+// False positive tests
+
+unsigned false1() {
+ int a = 10;
+ return a * (5 - 2 - 3); // no-warning
+}
+
+enum testenum { enum1 = 0, enum2 };
+unsigned false2() {
+ int a = 1234;
+ return enum1 + a; // no-warning
+}
+
+// Self assignments of unused variables are common false positives
+unsigned false3(int param, int param2) {
+ param = param; // no-warning
+
+ // if a self assigned variable is used later, then it should be reported still
+ param2 = param2; // expected-warning{{Assigned value is always the same as the existing value}}
+
+ unsigned nonparam = 5;
+
+ nonparam = nonparam; // expected-warning{{Assigned value is always the same as the existing value}}
+
+ return param2 + nonparam;
+}
+
+// Pseudo-constants (vars only read) and constants should not be reported
+unsigned false4() {
+ // Trivial constant
+ const int height = 1;
+ int c = 42;
+ test(height * c); // no-warning
+
+ // Pseudo-constant (never changes after decl)
+ int width = height;
+
+ return width * 10; // no-warning
+}
+
+// Block pseudoconstants
+void false4a() {
+ // Pseudo-constant
+ __block int a = 1;
+ int b = 10;
+ __block int c = 0;
+ b *= a; // no-warning
+
+ ^{
+ // Psuedoconstant block var
+ test(b * c); // no-warning
+
+ // Non-pseudoconstant block var
+ int d = 0;
+ test(b * d); // expected-warning{{The right operand to '*' is always 0}}
+ d = 5;
+ test(d);
+ }();
+
+ test(a + b);
+}
+
+// Static vars are common false positives
+int false5() {
+ static int test = 0;
+ int a = 56;
+ a *= test; // no-warning
+ test++;
+ return a;
+}
+
+// Non-local storage vars are considered false positives
+int globalInt = 1;
+int false6() {
+ int localInt = 23;
+
+ localInt /= globalInt;
+
+ return localInt;
+}
+
+// Check that assignments filter out false positives correctly
+int false7() {
+ int zero = 0; // pseudo-constant
+ int one = 1;
+
+ int a = 55;
+ a = a; // expected-warning{{Assigned value is always the same as the existing value}}
+ a = enum1 * a; // no-warning
+
+ int b = 123;
+ b = b; // no-warning
+
+ return a;
+}
+
+// Check truncations do not flag as self-assignments
+void false8() {
+ int a = 10000000;
+ a = (short)a; // no-warning
+ test(a);
+}
+
+// This test case previously flagged a warning at 'b == c' because the
+// analyzer previously allowed 'UnknownVal' as the index for ElementRegions.
+typedef struct RDar8431728_F {
+ int RDar8431728_A;
+ unsigned char *RDar8431728_B;
+ int RDar8431728_E[6];
+} RDar8431728_D;
+static inline int RDar8431728_C(RDar8431728_D * s, int n,
+ unsigned char **RDar8431728_B_ptr) {
+ int xy, wrap, pred, a, b, c;
+
+ xy = s->RDar8431728_E[n];
+ wrap = s->RDar8431728_A;
+
+ a = s->RDar8431728_B[xy - 1];
+ b = s->RDar8431728_B[xy - 1 - wrap];
+ c = s->RDar8431728_B[xy - wrap];
+
+ if (b == c) { // no-warning
+ pred = a;
+ } else {
+ pred = c;
+ }
+
+ *RDar8431728_B_ptr = &s->RDar8431728_B[xy];
+
+ return pred;
+}
+
+// <rdar://problem/8601243> - Don't warn on pointer arithmetic. This
+// is often idiomatic.
+unsigned rdar8601243_aux(unsigned n);
+void rdar8601243() {
+ char arr[100];
+ char *start = arr;
+ start = start + rdar8601243_aux(sizeof(arr) - (arr - start)); // no-warning
+ (void) start;
+}
+