[clang] [analyzer] Add std::variant checker (PR #66481)

via cfe-commits cfe-commits at lists.llvm.org
Thu Sep 21 03:40:26 PDT 2023


================
@@ -0,0 +1,349 @@
+// RUN: %clang %s -std=c++17 -Xclang -verify --analyze \
+// RUN:   -Xclang -analyzer-checker=core \
+// RUN:   -Xclang -analyzer-checker=debug.ExprInspection \
+// RUN:   -Xclang -analyzer-checker=core,alpha.core.StdVariant
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+class Foo{};
+
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(int);
+
+//helper functions
+void changeVariantType(std::variant<int, char> &v) {
+  v = 25;
+}
+
+void changesToInt(std::variant<int, char> &v);
+void changesToInt(std::variant<int, char> *v);
+
+void cannotChangePtr(const std::variant<int, char> &v);
+void cannotChangePtr(const std::variant<int, char> *v);
+
+char getUnknownChar();
+
+void swap(std::variant<int, char> &v1, std::variant<int, char> &v2) {
+  std::variant<int, char> tmp = v1;
+  v1 = v2;
+  v2 = tmp;
+}
+
+void cantDo(const std::variant<int, char>& v) {
+  std::variant<int, char> vtmp = v;
+  vtmp = 5;
+  int a = std::get<int> (vtmp);
+  (void*) a;
+}
+
+void changeVariantPtr(std::variant<int, char> *v) {
+  *v = 'c';
+}
+
+using var_t = std::variant<int, char>;
+using var_tt = var_t;
+using int_t = int;
+using char_t = char;
+
+
+//----------------------------------------------------------------------------//
+// std::get
+//----------------------------------------------------------------------------//
+void stdGetType() {
+  std::variant<int, char> v = 25;
+  int a = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void stdGetPointer() {
+  int *p = new int;
+  std::variant<int*, char> v = p;
+  int *a = std::get<int*>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *' not a 'char'}}
+  (void**)a;
+  (void*)c;
+  delete p;
+}
+
+void stdGetObject() {
+  std::variant<int, char, Foo> v = Foo{};
+  Foo f = std::get<Foo>(v);
+  int i = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'Foo' not an 'int'}}
+  (void*)i;
+}
+
+void stdGetPointerAndPointee() {
+  int a = 5;
+  std::variant<int, int*> v = &a;
+  int *b = std::get<int*>(v);
+  int c = std::get<int>(v); // expected-warning {{std::variant 'v' held an 'int *' not an 'int'}}
+  (void*)c;
+  (void**)b;
+}
+
+void variantHoldingVariant() {
+  std::variant<std::variant<int, char>, std::variant<char, int>> v = std::variant<int,char>(25);
+  std::variant<int, char> v1 = std::get<std::variant<int,char>>(v);
+  std::variant<char, int> v2 = std::get<std::variant<char,int>>(v); // expected-warning {{std::variant 'v' held a 'std::variant<int, char>' not a 'class std::variant<char, int>'}}
+}
+
+//----------------------------------------------------------------------------//
+// Constructors and assignments
+//----------------------------------------------------------------------------//
+void copyConstructor() {
+  std::variant<int, char> v = 25;
+  std::variant<int, char> t(v);
+  int a = std::get<int> (t);
+  char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void copyAssignmentOperator() {
+  std::variant<int, char> v = 25;
+  std::variant<int, char> t = 'c';
+  t = v;
+  int a = std::get<int> (t);
+  char c = std::get<char> (t); // expected-warning {{std::variant 't' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void assignmentOperator() {
+  std::variant<int, char> v = 25;
+  int a = std::get<int> (v);
+  (void*)a;
+  v = 'c';
+  char c = std::get<char>(v);
+  a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void typeChangeThreeTimes() {
+  std::variant<int, char, float> v = 25;
+  int a = std::get<int> (v);
+  (void*)a;
+  v = 'c';
+  char c = std::get<char>(v);
+  v = 25;
+  a = std::get<int>(v);
+  (void*)a;
+  v = 1.25f;
+  float f = std::get<float>(v);
+  a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'float' not an 'int'}}
+  (void*)a;
+  (void*)c;
+  f+0.f;
+}
+
+void defaultConstructor() {
+  std::variant<int, char> v;
+  int i = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)i;
+  (void*)c;
+}
+
+// Verify that we handle temporary objects correctly
+void temporaryObjectsConstructor() {
+  std::variant<int, char> v(std::variant<int, char>('c'));
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void temporaryObjectsAssignment() {
+  std::variant<int, char> v = std::variant<int, char>('c');
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+// Verify that we handle pointer types correctly
+void pointerTypeHeld() {
+  int *p = new int;
+  std::variant<int*, char> v = p;
+  int *a = std::get<int*>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int *' not a 'char'}}
+  (void**)a;
+  (void*)c;
+  delete p;
+}
+
+std::variant<int, char> get_unknown_variant();
+// Verify that the copy constructor is handles properly when the std::variant
+// has no previously activated type and we copy an object of unknown value in it.
+void copyFromUnknownVariant() {
+  std::variant<int, char> u = get_unknown_variant();
+  std::variant<int, char> v(u);
+  int a = std::get<int>(v); // no-waring
+  char c = std::get<char>(v); // no-warning
+  (void*)a;
+  (void*)c;
+}
+
+// Verify that the copy constructor is handles properly when the std::variant
+// has previously activated type and we copy an object of unknown value in it.
+void copyFromUnknownVariantBef() {
+  std::variant<int, char> v = 25;
+  std::variant<int, char> u = get_unknown_variant();
+  v = u;
+  int a = std::get<int>(v); // no-waring
+  char c = std::get<char>(v); // no-warning
+  (void*)a;
+  (void*)c;
+}
+
+//----------------------------------------------------------------------------//
+// typedef
+//----------------------------------------------------------------------------//
+
+void typefdefedVariant() {
+  var_t v = 25;
+  int a = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void typedefedTypedfefedVariant() {
+  var_tt v = 25;
+  int a = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void typedefedGet() {
+  std::variant<char, int> v = 25;
+  int a = std::get<int_t>(v);
+  char c = std::get<char_t>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void typedefedPack() {
+  std::variant<int_t, char_t> v = 25;
+  int a = std::get<int>(v);
+  char c = std::get<char>(v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+void fromVariable() {
+  char o = 'c';
+  std::variant<int, char> v(o);
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void unknowValueButKnownType() {
+  char o = getUnknownChar();
+  std::variant<int, char> v(o);
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void createPointer() {
+  std::variant<int, char> *v = new std::variant<int, char>(15);
+  int a = std::get<int>(*v);
+  char c = std::get<char>(*v); // expected-warning {{std::variant  held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+  delete v;
+}
+
+//----------------------------------------------------------------------------//
+// Passing std::variants to functions
+//----------------------------------------------------------------------------//
+
+// Verifying that we are not invalidating the memory region of a variant if
+// a non inlined or inlined function takes it as a constant reference or po'int'er
+void constNonInlineRef() {
+  std::variant<int, char> v = 'c';
+  cannotChangePtr(v);
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void contNonInlinePtr() {
+  std::variant<int, char> v = 'c';
+  cannotChangePtr(&v);
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void copyInAFunction() {
+  std::variant<int, char> v = 'c';
+  cantDo(v);
+  char c = std::get<char>(v);
+  int a = std::get<int>(v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+
+}
+
+// Verifying that we can keep track of the type stored in std::variant when
+// it is passed to an inlined function as a reference or pointer
+void changeThruPointers() {
+  std::variant<int, char> v = 15;
+  changeVariantPtr(&v);
+  char c = std::get<char> (v);
+  int a = std::get<int> (v); // expected-warning {{std::variant 'v' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void functionCallWithCopyAssignment() {
+  var_t v1 = 15;
+  var_t v2 = 'c';
+  swap(v1, v2);
+  int a = std::get<int> (v2);
+  (void*)a;
+  char c = std::get<char> (v1);
+  a = std::get<int> (v1); // expected-warning {{std::variant 'v1' held a 'char' not an 'int'}}
+  (void*)a;
+  (void*)c;
+}
+
+void inlineFunctionCall() {
+  std::variant<int, char> v = 'c';
+  changeVariantType(v);
+  int a = std::get<int> (v);
+  char c = std::get<char> (v); // expected-warning {{std::variant 'v' held an 'int' not a 'char'}}
+  (void*)a;
+  (void*)c;
+}
+
+// Verifying that we invalidate the mem region of std::variant when it is
+// passed as a non const reference or a po'int'er to a non inlined function.
----------------
DonatNagyE wrote:

Yet another `po'int'er`.

https://github.com/llvm/llvm-project/pull/66481


More information about the cfe-commits mailing list