
/**
 * MyClass1 contains an `int` and has well behaved `operator<`
 */
class MyClass1 {
public:
	MyClass1() : v(0) {};
	MyClass1(int _v) : v(_v) {};

	bool operator<(const MyClass1 &other) {
		return v < other.v;
	}

	operator bool() {
		return true;
	}

	int v;
};

/**
 * MyClass2 contains an `int` but has an unusual `operator<`
 */
class MyClass2 {
public:
	MyClass2() : v(0) {};
	MyClass2(int _v) : v(_v) {};

	MyClass2 operator<(const MyClass2 &other) {
		return MyClass2(other.v);
	}

	operator bool() {
		return true;
	}

	int v;
};

void test1(int x, int y, int z) {
	// built-in comparison
	if (x < y < z) {} // $ Alert // BAD
	if (x > y > z) {} // $ Alert // BAD
	if (x <= y <= z) {} // $ Alert // BAD
	if (x <= y <= z) {} // $ Alert // BAD
	if (x < y > z) {} // $ Alert // BAD
	if ((x < y) && (y < z)) {} // GOOD
	if (x < y && y < z) {} // GOOD

	if ((x + 1) < (y + 1) < (z + 1)) {} // $ Alert // BAD
	if (x < x + y < z) {} // $ Alert // BAD

	if ((x < y) < z) {} // GOOD (this is deliberately allowed)
	if (!(x < y < z)) {} // $ Alert // BAD

	// overloaded comparison
	{
		MyClass1 a, b, c;

		if (a < b < c) {} // $ MISSING: Alert // BAD (the overloaded `operator<` behaves like `<`) [NOT DETECTED]
	}

	// overloaded non-comparison
	{
		MyClass2 a, b, c;

		if (a < b < c) {} // GOOD (the overloaded `operator<` does not behave like `<`)
	}
}
