unions.cpp 3.2 KB
// RUN: %clang_analyze_cc1 -std=c++14 -analyzer-checker=core,unix.Malloc,debug.ExprInspection %s -analyzer-config eagerly-assume=false -verify

extern void clang_analyzer_eval(bool);
extern void clang_analyzer_warnIfReached();
extern "C" char *strdup(const char *s);

namespace PR14054_reduced {
  struct Definition;
  struct ParseNode {
    union {
      Definition *lexdef;
      ParseNode *data;
    } pn_u;
  };
  struct Definition : public ParseNode { };

  void CloneParseTree(ParseNode *opn, ParseNode *pn,  ParseNode *x) {
    // This used to cause an assertion failure because:
    // 1. The implicit operator= for unions assigns all members of the union,
    //    not just the active one (b/c there's no way to know which is active).
    // 2. RegionStore dutifully stored all the variants at the same offset;
    //    the last one won.
    // 3. We asked for the value of the first variant but got back a conjured
    //    symbol for the second variant.
    // 4. We ended up trying to add a base cast to a region of the wrong type.
    //
    // Now (at the time this test was added), we instead treat all variants of
    // a union as different offsets, but only allow one to be active at a time.
    *pn = *opn;
    x = pn->pn_u.lexdef->pn_u.lexdef;
  }
}

namespace PR14054_original {
  struct Definition;
  struct ParseNode {
    union {
      struct {
        union {}; // expected-warning {{does not declare anything}}
        Definition *lexdef;
      } name;
      class {
        int *target;
        ParseNode *data;
      } xmlpi;
    } pn_u;
  };
  struct Definition : public ParseNode { };

  void CloneParseTree(ParseNode *opn, ParseNode *pn,  ParseNode *x) {
    pn->pn_u = opn->pn_u;
    x = pn->pn_u.name.lexdef->pn_u.name.lexdef;
  }
}

namespace PR17596 {
  union IntOrString {
    int i;
    char *s;
  };

  extern void process(IntOrString);

  void test() {
    IntOrString uu;
    uu.s = strdup("");
    process(uu);
  }

  void testPositive() {
    IntOrString uu;
    uu.s = strdup("");
  } // expected-warning{{leak}}

  void testCopy() {
    IntOrString uu;
    uu.i = 4;
    clang_analyzer_eval(uu.i == 4); // expected-warning{{TRUE}}

    IntOrString vv;
    vv.i = 5;
    uu = vv;
    clang_analyzer_eval(uu.i == 5); // expected-warning{{TRUE}}
  }

  void testInvalidation() {
    IntOrString uu;
    uu.s = strdup("");

    IntOrString vv;
    char str[] = "abc";
    vv.s = str;

    uu = vv;
  } // expected-warning{{leak}}

  void testIndirectInvalidation() {
    IntOrString uu;
    char str[] = "abc";
    uu.s = str;

    clang_analyzer_eval(uu.s[0] == 'a'); // expected-warning{{TRUE}}

    process(uu);
    clang_analyzer_eval(uu.s[0] == 'a'); // expected-warning{{UNKNOWN}}
  }
}

namespace assume_union_contents {
union U {
  int x;
};

U get();

void test() {
  U u = get();
  int y = 0;
  if (u.x)
    y = 1;
  if (u.x)
    y = 1 / y; // no-warning
}
} // end namespace assume_union_contents

namespace pr37688_deleted_union_destructor {
struct S { ~S(); };
struct A {
  ~A() noexcept {}
  union {
    struct {
      S s;
    } ss;
  };
};
void foo() {
  A a;
} // no-crash
void bar() {
  foo();
  clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
}
} // end namespace pr37688_deleted_union_destructor