// semmle-extractor-options: --microsoft  --edg --target --edg linux_x86_64
typedef unsigned __int64 size_t;
void *malloc(size_t size);
void free(void *ptr);

// placement new
void* operator new(size_t _Size, void *_Where)
{
	// ...
}

// ---

class myClass
{
public:
	int x, y;
};

myClass *global_p1, *global_p2;

void f1()
{
	myClass *p1, *p2;

	p1 = new myClass;
	p2 = (myClass *)malloc(sizeof(myClass));

	global_p1 = p1;
	global_p2 = p2;
}

void f2()
{
	delete global_p1; // GOOD
	delete global_p2; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
}

void f3()
{
	free(global_p1); // $ Alert[cpp/new-free-mismatch] // BAD: new -> delete
	free(global_p2); // GOOD
}

void *my_malloc(size_t size)
{
	return malloc(size);
}

template<class T> void my_delete(T *t)
{
	if (t != 0)
	{
		delete t;
	}
}

int main()
{
	// straightforward cases
	{
		myClass *p1 = new myClass;
		myClass *p2 = new myClass[10];
		myClass *p3 = (myClass *)malloc(sizeof(myClass));

		delete p1; // GOOD
		delete [] p2; // GOOD
		delete p3; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
	}
	{
		myClass *p1 = new myClass;
		myClass *p2 = new myClass[10];
		myClass *p3 = (myClass *)malloc(sizeof(myClass));

		free(p1); // $ Alert[cpp/new-free-mismatch] // BAD: new -> free
		free(p2); // $ Alert[cpp/new-free-mismatch] // BAD: new[] -> free
		free(p3); // GOOD
	}

	// more complex flow
	f1();
	f2();
	f1();
	f3();

	// wrapped malloc / delete
	{
		myClass *p1 = (myClass *)my_malloc(sizeof(myClass));
		myClass *p2 = (myClass *)my_malloc(sizeof(myClass));

		delete p1; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
		free(p2); // GOOD
	}
	{
		myClass *p1 = new myClass;
		myClass *p2 = (myClass *)malloc(sizeof(myClass));

		my_delete(p1); // GOOD
		my_delete(p2); // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
	}

	// overwritten
	{
		myClass *p1 = new myClass;

		delete p1; // GOOD
		p1 = (myClass *)malloc(sizeof(myClass));

		free(p1); // GOOD
	}

	// placement new
	{
		void *v1 = malloc(sizeof(myClass));
		// ... check alignment of v1 ...
		myClass *p1 = new (v1) myClass();

		p1->~myClass();
		free(p1); // GOOD
	}

	return 0;
}

void *my_malloc_2(size_t size)
{
	void *alloc = malloc(size);

	return alloc;
}

void test2()
{
	void *a = my_malloc_2(10);
	void *b = my_malloc_2(10);

	free(a); // GOOD
	delete b; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
}

void *my_malloc_3(size_t size)
{
	void *alloc = malloc(size);
	void *mid = alloc;

	return mid;
}

void test3()
{
	void *a = my_malloc_3(10);
	void *b = my_malloc_3(10);

	free(a); // GOOD
	delete b; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
}

void test4(bool do_array_delete)
{
	myClass *mc = new myClass;
	myClass *mc_array = new myClass[1024];

	if (do_array_delete)
	{
		delete [] mc; // $ Alert[cpp/new-delete-array-mismatch] // BAD
		delete [] mc_array; // GOOD
	} else {
		delete mc; // GOOD
		delete mc_array; // $ Alert[cpp/new-array-delete-mismatch] // BAD
	}
}

void test5(bool do_array_delete)
{
	char *c_array = new char[32];
	char *c_array_ptr_2 = c_array;

	if (do_array_delete)
	{
		delete [] c_array_ptr_2; // GOOD
	} else {
		delete c_array_ptr_2; // $ Alert[cpp/new-array-delete-mismatch] // BAD
	}
}

void test6(bool do_array_delete)
{
	char *c_ptr_array[10] = {0};
	c_ptr_array[5] = new char;

	if (do_array_delete)
	{
		delete [] c_ptr_array[5]; // $ MISSING: Alert // BAD [NOT DETECTED]
	} else {
		delete c_ptr_array[5]; // GOOD
	}
}

myClass *global_mc = 0;

void test7(bool do_array_delete)
{
	// alloc
	if (global_mc == 0)
	{
		global_mc = new myClass;
	}

	// free
	if (global_mc != 0)
	{
		if (do_array_delete)
		{
			delete [] global_mc; // $ Alert[cpp/new-delete-array-mismatch] // BAD
		} else {
			delete global_mc; // GOOD
		}
	}
}

void test8(bool cond)
{
	void *a = 0, *b = 0, *c = 0;

	if (cond) {
		a = malloc(64);
		b = new int;
		c = new int[10];
	}

	free(a); // GOOD
	delete a; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete
	delete [] a; // $ Alert[cpp/new-free-mismatch] // BAD: malloc -> delete[]

	free(b); // $ Alert[cpp/new-free-mismatch] // BAD: new -> free
	delete b; // GOOD
	delete [] b; // $ Alert[cpp/new-delete-array-mismatch] // BAD: new -> delete[]

	free(c); // $ Alert[cpp/new-free-mismatch] // BAD: new[] -> free
	delete c; // $ Alert[cpp/new-array-delete-mismatch] // BAD: new[] -> delete
	delete [] c; // GOOD
}

void test9()
{
	void *ptr;

	ptr = new int;
	delete ptr; // GOOD

	ptr = new int[10];
	delete [] ptr; // GOOD

	ptr = malloc(sizeof(int));
	free(ptr); // GOOD
}

class ClassWithMembers
{
public:
	ClassWithMembers()
	{
		a = new int;
		b = new int;
		c = new int;
	}

	~ClassWithMembers()
	{
		delete a; // GOOD
		delete [] b; // $ Alert[cpp/new-delete-array-mismatch] // BAD: new -> delete[]
		free(c); // $ Alert[cpp/new-free-mismatch] // BAD: new -> free
	}

private:
	int *a, *b, *c;
};

// ---

struct map_cell
{
	char ch;
	int col;
};
map_cell *map;

static void map_init()
{
	map = new map_cell[30 * 30];
}

static void map_shutdown()
{
	delete map; // $ Alert[cpp/new-array-delete-mismatch] // BAD: new[] -> delete
	map = 0;
}

// ---

class Test10
{
public:
	Test10() : data(new char[10])
	{
	}

	~Test10()
	{
		delete data; // $ Alert[cpp/new-array-delete-mismatch] // BAD: new[] -> delete
	}

	char *data;
};

class Test11
{
public:
	Test11()
	{
		data = new char[10];
	}

	void resize(int size)
	{
		if (size > 0)
		{
			delete [] data; // GOOD
			data = new char[size];
		}
	}

	~Test11()
	{
		delete data; // $ Alert[cpp/new-array-delete-mismatch] // BAD: new[] -> delete
	}

	char *data;
};

// ---

int *z;

void test12(bool cond)
{
	int *x, *y;

	x = new int();
	delete x; // GOOD
	x = (int *)malloc(sizeof(int));
	free(x); // GOOD

	if (cond)
	{
		y = new int();
		z = new int();
	} else {
		y = (int *)malloc(sizeof(int));
		z = (int *)malloc(sizeof(int));
	}

	// ...

	if (cond)
	{
		delete y; // GOOD
		delete z; // GOOD
	} else {
		free(y); // GOOD
		free(z); // GOOD
	}
}

// ---

class MyBuffer13
{
public:
	MyBuffer13(int size)
	{
		buffer = (char *)malloc(size * sizeof(char));
	}

	~MyBuffer13()
	{
		free(buffer); // GOOD
	}

	char *getBuffer() // note: this should not be considered an allocation function
	{
		return buffer;
	}

private:
	char *buffer;
};

class MyPointer13
{
public:
	MyPointer13(char *_pointer) : pointer(_pointer)
	{
	}

	MyPointer13(MyBuffer13 &buffer) : pointer(buffer.getBuffer())
	{
	}

	char *getPointer() // note: this should not be considered an allocation function
	{
		return pointer;
	}

private:
	char *pointer;
};

void test13()
{
	MyBuffer13 myBuffer(100);
	MyPointer13 myPointer2(myBuffer);
	MyPointer13 myPointer3(new char[100]);

	delete myPointer3.getPointer(); // GOOD
}

char *strdup(const char *s1);
char *strndup(const char *s1, size_t n);
wchar_t* wcsdup(const wchar_t* s1);

void test14()
{
	char *s1 = strdup("string");
	char *s2 = strdup("string");
	char *s3 = strndup("string", 3);
	char *s4 = strndup("string", 3);
	wchar_t *s5 = wcsdup(L"string");
	wchar_t *s6 = wcsdup(L"string");

	delete s1; // $ Alert[cpp/new-free-mismatch] // BAD: strdup -> delete
	free(s2); // GOOD
	delete s3; // $ Alert[cpp/new-free-mismatch] // BAD: strndup -> delete
	free(s4); // GOOD
	delete s5; // $ Alert[cpp/new-free-mismatch] // BAD: wcsdup -> delete
	free(s6); // GOOD
}
