gchatelet updated this revision to Diff 175030. gchatelet marked an inline comment as done. gchatelet added a comment.
- Refactored the code to make it simpler, added more tests Repository: rCTE Clang Tools Extra https://reviews.llvm.org/D53488 Files: clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.h docs/ReleaseNotes.rst docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.rst test/clang-tidy/cppcoreguidelines-narrowing-conversions-extras.cpp test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp
Index: test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp =================================================================== --- test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp +++ test/clang-tidy/cppcoreguidelines-narrowing-conversions.cpp @@ -1,4 +1,4 @@ -// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t +// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t -- -- -target x86_64-unknown-linux -fsigned-char float ceil(float); namespace std { @@ -9,47 +9,284 @@ namespace floats { struct ConvertsToFloat { - operator float() const { return 0.5; } + operator float() const { return 0.5f; } }; -float operator "" _Pa(unsigned long long); +float operator"" _float(unsigned long long); -void not_ok(double d) { +void narrow_fp_to_int_not_ok(double d) { int i = 0; i = d; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] i = 0.5f; - // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i = static_cast<float>(d); // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i = ConvertsToFloat(); // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] - i = 15_Pa; + i = 15_float; // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] -} - -void not_ok_binary_ops(double d) { - int i = 0; + i += d; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] i += 0.5; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] i += 0.5f; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] - i += d; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] - // We warn on the following even though it's not dangerous because there is no - // reason to use a double literal here. - // TODO(courbet): Provide an automatic fix. - i += 2.0; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] - i += 2.0f; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] - + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i *= 0.5f; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i /= 0.5f; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] i += (double)0.5f; - // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: narrowing conversion from 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + i += 2.0; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + i += 2.0f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] +} + +double operator"" _double(unsigned long long); + +float narrow_double_to_float_return() { + return 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. +} + +void narrow_double_to_float_ok(double d) { + float f; + f = d; + f = 15_double; +} + +void narrow_fp_constants() { + float f; + f = 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + + f = __builtin_huge_valf(); // max float is not narrowing. + f = -__builtin_huge_valf(); // -max float is not narrowing. + f = __builtin_inff(); // float infinity is not narrowing. + f = __builtin_nanf("0"); // float NaN is not narrowing. + + f = __builtin_huge_val(); // max double is not within-range of float. + f = -__builtin_huge_val(); // -max double is not within-range of float. + f = __builtin_inf(); // double infinity is not within-range of float. + f = __builtin_nan("0"); // double NaN is not narrowing. +} + +void narrow_double_to_float_not_ok_binary_ops(double d) { + float f; + f += 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + f += 2.0; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + f *= 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + f /= 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + f += (double)0.5f; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + f += d; // We do not warn about floating point narrowing by default. +} + +void narrow_fp_constant_to_bool_not_ok() { + bool b1 = 1.0; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'double' to 'bool' [cppcoreguidelines-narrowing-conversions] + bool b2 = 1.0f; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant 'float' to 'bool' [cppcoreguidelines-narrowing-conversions] +} + +void narrow_integer_to_floating() { + { + long long ll; // 64 bits + float f = ll; // doesn't fit in 24 bits + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'long long' to 'float' [cppcoreguidelines-narrowing-conversions] + double d = ll; // doesn't fit in 53 bits. + // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: narrowing conversion from 'long long' to 'double' [cppcoreguidelines-narrowing-conversions] + } + { + int i; // 32 bits + float f = i; // doesn't fit in 24 bits + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions] + double d = i; // fits in 53 bits. + } + { + short n1, n2; + float f = n1 + n2; // 'n1 + n2' is of type 'int' because of integer rules + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: narrowing conversion from 'int' to 'float' [cppcoreguidelines-narrowing-conversions] + } + { + short s; // 16 bits + float f = s; // fits in 24 bits + double d = s; // fits in 53 bits. + } +} + +void narrow_integer_to_unsigned_integer_is_ok() { + char c; + short s; + int i; + long l; + long long ll; + + unsigned char uc; + unsigned short us; + unsigned int ui; + unsigned long ul; + unsigned long long ull; + + ui = c; + uc = s; + uc = i; + uc = l; + uc = ll; + + uc = uc; + uc = us; + uc = ui; + uc = ul; + uc = ull; +} + +void narrow_integer_to_signed_integer_is_not_ok() { + char c; + short s; + int i; + long l; + long long ll; + + unsigned char uc; + unsigned short us; + unsigned int ui; + unsigned long ul; + unsigned long long ull; + + c = c; + c = s; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = i; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = l; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = ll; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + c = uc; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned char' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = us; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned short' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = ui; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = ul; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + c = ull; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + i = c; + i = s; + i = i; + i = l; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + i = ll; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + i = uc; + i = us; + i = ui; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned int' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + i = ul; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + i = ull; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'unsigned long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + ll = c; + ll = s; + ll = i; + ll = l; + ll = ll; + + ll = uc; + ll = us; + ll = ui; + ll = ul; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions] + ll = ull; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} + +void narrow_constant_to_unsigned_integer_is_ok() { + unsigned char uc1 = 0; + unsigned char uc2 = 255; + unsigned char uc3 = -1; // unsigned dst type is well defined. + unsigned char uc4 = 256; // unsigned dst type is well defined. + unsigned short us1 = 0; + unsigned short us2 = 65535; + unsigned short us3 = -1; // unsigned dst type is well defined. + unsigned short us4 = 65536; // unsigned dst type is well defined. +} + +void narrow_constant_to_signed_integer_is_not_ok() { + char c1 = -128; + char c2 = 127; + char c3 = -129; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + char c4 = 128; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + short s1 = -32768; + short s2 = 32767; + short s3 = -32769; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value -32769 (0xFFFF7FFF) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions] + short s4 = 32768; + // CHECK-MESSAGES: :[[@LINE-1]]:14: warning: narrowing conversion from constant value 32768 (0x00008000) of type 'int' to signed type 'short' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} + +void narrow_conditional_operator_contant_to_unsigned_is_ok(bool b) { + // conversion to unsigned dst type is well defined. + unsigned char c1 = b ? 1 : 0; + unsigned char c2 = b ? 1 : 256; + unsigned char c3 = b ? -1 : 0; +} + +void narrow_conditional_operator_contant_to_signed_is_not_ok(bool b) { + char uc1 = b ? 1 : 0; + char uc2 = b ? 1 : 128; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + char uc3 = b ? -129 : 0; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: narrowing conversion from constant value -129 (0xFFFFFF7F) of type 'int' to signed type 'char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + unsigned long long ysize; + long long mirror = b ? -1 : ysize - 1; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: narrowing conversion from constant value 18446744073709551615 (0xFFFFFFFFFFFFFFFF) of type 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions] + // CHECK-MESSAGES: :[[@LINE-2]]:37: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} + +void narrow_constant_to_floating_point() { + float f_ok = 1ULL << 24; // fits in 24 bits mantissa. + float f_not_ok = (1ULL << 24) + 1ULL; // doesn't fit in 24 bits mantissa. + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: narrowing conversion from constant value 16777217 of type 'unsigned long long' to 'float' [cppcoreguidelines-narrowing-conversions] + double d_ok = 1ULL << 53; // fits in 53 bits mantissa. + double d_not_ok = (1ULL << 53) + 1ULL; // doesn't fit in 53 bits mantissa. + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: narrowing conversion from constant value 9007199254740993 of type 'unsigned long long' to 'double' [cppcoreguidelines-narrowing-conversions] +} + +void casting_integer_to_bool_is_ok() { + int i; + while (i) { + } + for (; i;) { + } + if (i) { + } +} + +void casting_float_to_bool_is_not_ok() { + float f; + while (f) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions] + } + for (; f;) { + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions] + } + if (f) { + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'float' to 'bool' [cppcoreguidelines-narrowing-conversions] + } +} + +void legitimate_comparison_do_not_warn(unsigned long long size) { + for (int i = 0; i < size; ++i) { + } } void ok(double d) { @@ -89,15 +326,19 @@ void template_context() { f(1, 2); + f(1, .5f); f(1, .5); + f(1, .5l); } #define DERP(i, j) (i += j) void macro_context() { int i = 0; DERP(i, 2); + DERP(i, .5f); DERP(i, .5); + DERP(i, .5l); } -} // namespace floats +} // namespace floats Index: test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-narrowing-conversions-unsigned-char.cpp @@ -0,0 +1,82 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t -- -- -target x86_64-unknown-linux -funsigned-char + +void narrow_integer_to_unsigned_integer_is_ok() { + signed char sc; + short s; + int i; + long l; + long long ll; + + char c; + unsigned short us; + unsigned int ui; + unsigned long ul; + unsigned long long ull; + + ui = sc; + c = s; + c = i; + c = l; + c = ll; + + c = c; + c = us; + c = ui; + c = ul; + c = ull; +} + +void narrow_integer_to_signed_integer_is_not_ok() { + signed char sc; + short s; + int i; + long l; + long long ll; + + char c; + unsigned short us; + unsigned int ui; + unsigned long ul; + unsigned long long ull; + + sc = sc; + sc = s; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'short' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = i; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = l; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = ll; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'long long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + + sc = c; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'char' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = us; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned short' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = ui; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = ul; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] + sc = ull; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} + +void narrow_constant_to_unsigned_integer_is_ok() { + char c1 = -128; // unsigned dst type is well defined. + char c2 = 127; // unsigned dst type is well defined. + char c3 = -129; // unsigned dst type is well defined. + char c4 = 128; // unsigned dst type is well defined. + unsigned char uc1 = 0; + unsigned char uc2 = 255; + unsigned char uc3 = -1; // unsigned dst type is well defined. + unsigned char uc4 = 256; // unsigned dst type is well defined. + signed char sc = 128; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: narrowing conversion from constant value 128 (0x00000080) of type 'int' to signed type 'signed char' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} + +void narrow_conditional_operator_contant_to_unsigned_is_ok(bool b) { + // conversion to unsigned char type is well defined. + char c1 = b ? 1 : 0; + char c2 = b ? 1 : 256; + char c3 = b ? -1 : 0; +} Index: test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-narrowing-conversions-long-is-32bits.cpp @@ -0,0 +1,22 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t -- -- -target x86_64-unknown-linux -m32 + +static_assert(sizeof(int) * 8 == 32, "int is 32-bits"); +static_assert(sizeof(long) * 8 == 32, "long is 32-bits"); +static_assert(sizeof(long long) * 8 == 64, "long long is 64-bits"); + +void narrow_integer_to_signed_integer_is_not_ok() { + int i; // i.e. int32_t + long l; // i.e. int32_t + long long ll; // i.e. int64_t + + unsigned int ui; // i.e. uint32_t + unsigned long ul; // i.e. uint32_t + unsigned long long ull; // i.e. uint64_t + + i = l; // int and long are the same type. + i = ll; // int64_t does not fit in an int32_t + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'long long' to signed type 'int' is implementation-defined [cppcoreguidelines-narrowing-conversions] + ll = ul; // uint32_t fits into int64_t + ll = ull; // uint64_t does not fit in an int64_t + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'unsigned long long' to signed type 'long long' is implementation-defined [cppcoreguidelines-narrowing-conversions] +} Index: test/clang-tidy/cppcoreguidelines-narrowing-conversions-extras.cpp =================================================================== --- /dev/null +++ test/clang-tidy/cppcoreguidelines-narrowing-conversions-extras.cpp @@ -0,0 +1,58 @@ +// RUN: %check_clang_tidy %s cppcoreguidelines-narrowing-conversions %t -config="{CheckOptions: [{key: "cppcoreguidelines-narrowing-conversions.WarnOnFloatingPointNarrowingConversion", value: 1}]}" -- -target x86_64-unknown-linux -fsigned-char + +namespace floats { + +void narrow_constant_floating_point_to_int_not_ok(double d) { + int i = 0; + i += 0.5; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + i += 0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + i *= 0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + i /= 0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] + i += (double)0.5f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + i += 2.0; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'double' to 'int' [cppcoreguidelines-narrowing-conversions] + i += 2.0f; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from constant 'float' to 'int' [cppcoreguidelines-narrowing-conversions] +} + +double operator"" _double(unsigned long long); + +float narrow_double_to_float_return() { + return 0.5; +} + +void narrow_double_to_float_not_ok(double d) { + float f; + f = d; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = 15_double; + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f += d; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: narrowing conversion from 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = narrow_double_to_float_return(); +} + +void narrow_fp_constants() { + float f; + f = 0.5; // [dcl.init.list] 7.2 : in-range fp constant to narrower float is not a narrowing. + + f = __builtin_huge_valf(); // max float is not narrowing. + f = -__builtin_huge_valf(); // -max float is not narrowing. + f = __builtin_inff(); // float infinity is not narrowing. + f = __builtin_nanf("0"); // float NaN is not narrowing. + + f = __builtin_huge_val(); // max double is not within-range of float. + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = -__builtin_huge_val(); // -max double is not within-range of float. + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = __builtin_inf(); // double infinity is not within-range of float. + // CHECK-MESSAGES: :[[@LINE-1]]:7: warning: narrowing conversion from constant 'double' to 'float' [cppcoreguidelines-narrowing-conversions] + f = __builtin_nan("0"); // double NaN is not narrowing. +} + +} // namespace floats Index: docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.rst =================================================================== --- docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.rst +++ docs/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.rst @@ -10,13 +10,48 @@ This rule is part of the "Expressions and statements" profile of the C++ Core Guidelines, corresponding to rule ES.46. See -https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Res-narrowing. +https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es46-avoid-lossy-narrowing-truncating-arithmetic-conversions. -We enforce only part of the guideline, more specifically, we flag: - - All floating-point to integer conversions that are not marked by an explicit - cast (c-style or ``static_cast``). For example: ``int i = 0; i += 0.1;``, +We enforce only part of the guideline, more specifically, we flag narrowing conversions from: + - an integer to a narrower integer (e.g. ``char`` to ``unsigned char``), + - an integer to a narrower floating-point (e.g. ``uint64_t`` to ``float``), + - a floating-point to an integer (e.g. ``double`` to ``int``), + - a floating-point to a narrower floating-point (e.g. ``double`` to ``float``) + if WarnOnFloatingPointNarrowingConversion Option is set. + +This check will flag: + - All narrowing conversions that are not marked by an explicit cast (c-style or + ``static_cast``). For example: ``int i = 0; i += 0.1;``, ``void f(int); f(0.1);``, - - All applications of binary operators where the left-hand-side is an integer - and the right-hand-size is a floating-point. For example: - ``int i; i+= 0.1;``. + - All applications of binary operators with a narrowing conversions. + For example: ``int i; i+= 0.1;``. + + + Options + ------- + + .. option:: WarnOnFloatingPointNarrowingConversion + + When non-zero, the check will warn on narrowing floating point conversion + (e.g. ``double`` to ``float``). `0` by default. + +FAQ +--- + + - What does "narrowing conversion from 'int' to 'float'" mean? + +An IEEE754 Floating Point number can represent all integer values in the range +[-2^PrecisionBits, 2^PrecisionBits] where PrecisionBits is the number of bits in +the mantissa. + +For ``float`` this would be [-2^23, 2^23], where ``int`` can represent values in +the range [-2^31, 2^31-1]. + + - What does "implementation-defined" mean? +You may have encountered messages like so "narrowing conversion from +'unsigned int' to signed type 'int' is implementation-defined". +The C/C++ standard do not mandate two’s complement for signed integers, as +such the compiler is free to define what is the semantic for converting an +unsigned integer to signed integer. Clang's implementation uses the two’s +complement format. Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -205,6 +205,14 @@ <clang-tidy/checks/readability-redundant-smartptr-get>` check does not warn about calls inside macros anymore by default. +- The :doc:`cppcoreguidelines-narrowing-conversions + <clang-tidy/checks/cppcoreguidelines-narrowing-conversions>` check now + detects more narrowing conversions: + - integer to narrower signed integer (this is compiler implementation defined), + - integer - floating point narrowing conversions, + - floating point - integer narrowing conversions, + - constants with narrowing conversions (even in ternary operator). + Improvements to include-fixer ----------------------------- Index: clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.h =================================================================== --- clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.h +++ clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.h @@ -24,10 +24,72 @@ /// http://clang.llvm.org/extra/clang-tidy/checks/cppcoreguidelines-narrowing-conversions.html class NarrowingConversionsCheck : public ClangTidyCheck { public: - NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context) - : ClangTidyCheck(Name, Context) {} + NarrowingConversionsCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void diagNarrowType(SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void diagNarrowTypeToSignedInt(SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void diagNarrowIntegerConstant(SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs, const llvm::APSInt &Value); + + void diagNarrowIntegerConstantToSignedInt(SourceLocation SourceLoc, + const Expr &Lhs, const Expr &Rhs, + const llvm::APSInt &Value, + const uint64_t HexBits); + + void diagNarrowConstant(SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void diagNarrowTypeOrConstant(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleIntegralCast(const ASTContext &Context, SourceLocation SourceLoc, + const Expr &Lhs, const Expr &Rhs); + + void handleIntegralToBoolean(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleIntegralToFloating(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleFloatingToIntegral(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleFloatingToBoolean(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleBooleanToSignedIntegral(const ASTContext &Context, + SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs); + + void handleFloatingCast(const ASTContext &Context, SourceLocation SourceLoc, + const Expr &Lhs, const Expr &Rhs); + + void handleBinaryOperator(const ASTContext &Context, SourceLocation SourceLoc, + const Expr &Lhs, const Expr &Rhs); + + bool handleConditionalOperator(const ASTContext &Context, const Expr &Lhs, + const Expr &Rhs); + + void handleImplicitCast(const ASTContext &Context, + const ImplicitCastExpr &Cast); + + void handleBinaryOperator(const ASTContext &Context, + const BinaryOperator &Op); + + const bool WarnOnFloatingPointNarrowingConversion; }; } // namespace cppcoreguidelines Index: clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp =================================================================== --- clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp +++ clang-tidy/cppcoreguidelines/NarrowingConversionsCheck.cpp @@ -9,60 +9,392 @@ #include "NarrowingConversionsCheck.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" #include "clang/ASTMatchers/ASTMatchFinder.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" + +#include <cstdint> using namespace clang::ast_matchers; namespace clang { namespace tidy { namespace cppcoreguidelines { -// FIXME: Check double -> float truncation. Pay attention to casts: +NarrowingConversionsCheck::NarrowingConversionsCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnOnFloatingPointNarrowingConversion( + Options.get("WarnOnFloatingPointNarrowingConversion", 0)) {} + void NarrowingConversionsCheck::registerMatchers(MatchFinder *Finder) { // ceil() and floor() are guaranteed to return integers, even though the type // is not integral. - const auto IsCeilFloorCall = callExpr(callee(functionDecl( - hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor")))); - - const auto IsFloatExpr = - expr(hasType(realFloatingPointType()), unless(IsCeilFloorCall)); + const auto IsCeilFloorCallExpr = expr(callExpr(callee(functionDecl( + hasAnyName("::ceil", "::std::ceil", "::floor", "::std::floor"))))); - // casts: + // Casts: // i = 0.5; // void f(int); f(0.5); - Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(isInteger()), - hasSourceExpression(IsFloatExpr), - unless(hasParent(castExpr())), - unless(isInTemplateInstantiation())) - .bind("cast"), - this); + Finder->addMatcher( + implicitCastExpr(hasImplicitDestinationType(builtinType()), + hasSourceExpression(hasType(builtinType())), + unless(hasSourceExpression(IsCeilFloorCallExpr)), + unless(hasParent(castExpr())), + unless(isInTemplateInstantiation())) + .bind("cast"), + this); // Binary operators: // i += 0.5; - Finder->addMatcher( - binaryOperator(isAssignmentOperator(), - // The `=` case generates an implicit cast which is covered - // by the previous matcher. - unless(hasOperatorName("=")), - hasLHS(hasType(isInteger())), hasRHS(IsFloatExpr), - unless(isInTemplateInstantiation())) - .bind("op"), - this); + Finder->addMatcher(binaryOperator(isAssignmentOperator(), + hasLHS(expr(hasType(builtinType()))), + hasRHS(expr(hasType(builtinType()))), + unless(hasRHS(IsCeilFloorCallExpr)), + unless(isInTemplateInstantiation()), + // The `=` case generates an implicit cast + // which is covered by the previous matcher. + unless(hasOperatorName("="))) + .bind("binary_op"), + this); } -void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { - if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("op")) { - if (Op->getBeginLoc().isMacroID()) +static const BuiltinType *getBuiltinType(const Expr &E) { + return E.getType().getCanonicalType().getTypePtr()->getAs<BuiltinType>(); +} + +static QualType getUnqualifiedType(const Expr &E) { + return E.getType().getUnqualifiedType(); +} + +static APValue getConstantExprValue(const ASTContext &Ctx, const Expr &E) { + llvm::APSInt IntegerConstant; + if (E.isIntegerConstantExpr(IntegerConstant, Ctx)) + return APValue(IntegerConstant); + APValue Constant; + if (Ctx.getLangOpts().CPlusPlus && E.isCXX11ConstantExpr(Ctx, &Constant)) + return Constant; + return {}; +} + +static bool getIntegerConstantExprValue(const ASTContext &Context, + const Expr &E, llvm::APSInt &Value) { + APValue Constant = getConstantExprValue(Context, E); + if (!Constant.isInt()) + return false; + Value = Constant.getInt(); + return true; +} + +namespace { + +struct IntegerRange { + bool Contains(const IntegerRange &From) const { + return llvm::APSInt::compareValues(Lower, From.Lower) <= 0 && + llvm::APSInt::compareValues(Upper, From.Upper) >= 0; + } + + bool Contains(const llvm::APSInt &Value) const { + return llvm::APSInt::compareValues(Lower, Value) <= 0 && + llvm::APSInt::compareValues(Upper, Value) >= 0; + } + + llvm::APSInt Lower; + llvm::APSInt Upper; +}; + +} // namespace + +static IntegerRange createFromType(const ASTContext &Context, + const BuiltinType &T) { + if (T.isFloatingPoint()) { + unsigned PrecisionBits = llvm::APFloatBase::semanticsPrecision( + Context.getFloatTypeSemantics(T.desugar())); + // Contrary to two's complement integer, floating point values are + // symmetric and have the same number of positive and negative values. + // The range of valid integer for a floating point value is: + // [-2^PrecisionBits, 2^PrecisionBits] + + // Values are created with PrecisionBits plus two bits: + // - One to express the missing negative value of 2's complement + // representation. + // - One for the sign. + llvm::APSInt UpperValue(PrecisionBits + 2, /*isUnsigned*/ false); + UpperValue.setBit(PrecisionBits); + llvm::APSInt LowerValue(PrecisionBits + 2, /*isUnsigned*/ false); + LowerValue.setBit(PrecisionBits); + LowerValue.setSignBit(); + return {LowerValue, UpperValue}; + } + assert(T.isInteger() && "Unexpected builtin type"); + uint64_t TypeSize = Context.getTypeSize(&T); + bool IsUnsignedInteger = T.isUnsignedInteger(); + return {llvm::APSInt::getMinValue(TypeSize, IsUnsignedInteger), + llvm::APSInt::getMaxValue(TypeSize, IsUnsignedInteger)}; +} + +static bool isWideEnoughToHold(const ASTContext &Context, + const BuiltinType &FromType, + const BuiltinType &ToType) { + IntegerRange FromIntegerRange = createFromType(Context, FromType); + IntegerRange ToIntegerRange = createFromType(Context, ToType); + return ToIntegerRange.Contains(FromIntegerRange); +} + +static bool isWideEnoughToHold(const ASTContext &Context, + const llvm::APSInt &IntegerConstant, + const BuiltinType &ToType) { + IntegerRange ToIntegerRange = createFromType(Context, ToType); + return ToIntegerRange.Contains(IntegerConstant); +} + +static llvm::SmallString<64> getValueAsString(const llvm::APSInt &Value, + uint64_t HexBits) { + llvm::SmallString<64> Str; + Value.toString(Str, 10); + if (HexBits > 0) { + Str.append(" (0x"); + llvm::SmallString<32> HexValue; + Value.toStringUnsigned(HexValue, 16); + for (size_t I = HexValue.size(); I < (HexBits / 4); ++I) + Str.append("0"); + Str.append(HexValue); + Str.append(")"); + } + return Str; +} + +void NarrowingConversionsCheck::diagNarrowType(SourceLocation SourceLoc, + const Expr &Lhs, + const Expr &Rhs) { + diag(SourceLoc, "narrowing conversion from %0 to %1") + << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); +} + +void NarrowingConversionsCheck::diagNarrowTypeToSignedInt( + SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs) { + diag(SourceLoc, "narrowing conversion from %0 to signed type %1 is " + "implementation-defined") + << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); +} + +void NarrowingConversionsCheck::diagNarrowIntegerConstant( + SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, + const llvm::APSInt &Value) { + diag(SourceLoc, + "narrowing conversion from constant value %0 of type %1 to %2") + << getValueAsString(Value, /*NoHex*/ 0) << getUnqualifiedType(Rhs) + << getUnqualifiedType(Lhs); +} + +void NarrowingConversionsCheck::diagNarrowIntegerConstantToSignedInt( + SourceLocation SourceLoc, const Expr &Lhs, const Expr &Rhs, + const llvm::APSInt &Value, const uint64_t HexBits) { + diag(SourceLoc, "narrowing conversion from constant value %0 of type %1 " + "to signed type %2 is implementation-defined") + << getValueAsString(Value, HexBits) << getUnqualifiedType(Rhs) + << getUnqualifiedType(Lhs); +} + +void NarrowingConversionsCheck::diagNarrowConstant(SourceLocation SourceLoc, + const Expr &Lhs, + const Expr &Rhs) { + diag(SourceLoc, "narrowing conversion from constant %0 to %1") + << getUnqualifiedType(Rhs) << getUnqualifiedType(Lhs); +} + +void NarrowingConversionsCheck::diagNarrowTypeOrConstant( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + APValue Constant = getConstantExprValue(Context, Rhs); + if (Constant.isInt()) + return diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, Constant.getInt()); + if (Constant.isFloat()) + return diagNarrowConstant(SourceLoc, Lhs, Rhs); + return diagNarrowType(SourceLoc, Lhs, Rhs); +} + +void NarrowingConversionsCheck::handleIntegralCast(const ASTContext &Context, + SourceLocation SourceLoc, + const Expr &Lhs, + const Expr &Rhs) { + const BuiltinType *ToType = getBuiltinType(Lhs); + // Conversions to unsigned integer is well defined so no warning is issued. + // "The resulting value is the smallest unsigned value equal to the source + // value modulo 2^n where n is the number of bits used to represent the + // destination type." + if (ToType->isUnsignedInteger()) + return; + const BuiltinType *FromType = getBuiltinType(Rhs); + llvm::APSInt IntegerConstant; + if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) { + if (!isWideEnoughToHold(Context, IntegerConstant, *ToType)) + diagNarrowIntegerConstantToSignedInt(SourceLoc, Lhs, Rhs, IntegerConstant, + Context.getTypeSize(FromType)); + return; + } + if (!isWideEnoughToHold(Context, *FromType, *ToType)) + diagNarrowTypeToSignedInt(SourceLoc, Lhs, Rhs); +} + +void NarrowingConversionsCheck::handleIntegralToBoolean( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + // Conversion from Integral to Bool value is well defined. +} + +void NarrowingConversionsCheck::handleIntegralToFloating( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + const BuiltinType *ToType = getBuiltinType(Lhs); + llvm::APSInt IntegerConstant; + if (getIntegerConstantExprValue(Context, Rhs, IntegerConstant)) { + if (!isWideEnoughToHold(Context, IntegerConstant, *ToType)) + diagNarrowIntegerConstant(SourceLoc, Lhs, Rhs, IntegerConstant); + return; + } + const BuiltinType *FromType = getBuiltinType(Rhs); + if (!isWideEnoughToHold(Context, *FromType, *ToType)) + diagNarrowType(SourceLoc, Lhs, Rhs); +} + +void NarrowingConversionsCheck::handleFloatingToIntegral( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs); +} + +void NarrowingConversionsCheck::handleFloatingToBoolean( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + return diagNarrowTypeOrConstant(Context, SourceLoc, Lhs, Rhs); +} + +void NarrowingConversionsCheck::handleBooleanToSignedIntegral( + const ASTContext &Context, SourceLocation SourceLoc, const Expr &Lhs, + const Expr &Rhs) { + // Conversion from Bool to SignedIntegral value is well defined. +} + +void NarrowingConversionsCheck::handleFloatingCast(const ASTContext &Context, + SourceLocation SourceLoc, + const Expr &Lhs, + const Expr &Rhs) { + if (WarnOnFloatingPointNarrowingConversion) { + const BuiltinType *ToType = getBuiltinType(Lhs); + APValue Constant = getConstantExprValue(Context, Rhs); + if (Constant.isFloat()) { + // From http://eel.is/c++draft/dcl.init.list#7.2 + // Floating point constant narrowing only takes place when the value is + // not within destination range. We convert the value to the destination + // type and check if the resulting value is infinity. + llvm::APFloat Tmp = Constant.getFloat(); + bool UnusedLosesInfo; + Tmp.convert(Context.getFloatTypeSemantics(ToType->desugar()), + llvm::APFloatBase::rmNearestTiesToEven, &UnusedLosesInfo); + if (Tmp.isInfinity()) + diagNarrowConstant(SourceLoc, Lhs, Rhs); return; - diag(Op->getOperatorLoc(), "narrowing conversion from %0 to %1") - << Op->getRHS()->getType() << Op->getLHS()->getType(); + } + const BuiltinType *FromType = getBuiltinType(Rhs); + if (ToType->getKind() < FromType->getKind()) + diagNarrowType(SourceLoc, Lhs, Rhs); + } +} + +void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, + SourceLocation SourceLoc, + const Expr &Lhs, + const Expr &Rhs) { + assert(!Lhs.isInstantiationDependent() && !Rhs.isInstantiationDependent() && + "Dependent types must be check before calling this function"); + const BuiltinType *LhsType = getBuiltinType(Lhs); + const BuiltinType *RhsType = getBuiltinType(Rhs); + if (RhsType == nullptr || LhsType == nullptr) + return; + if (RhsType->getKind() == BuiltinType::Bool && LhsType->isSignedInteger()) + return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isInteger() && LhsType->getKind() == BuiltinType::Bool) + return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isInteger() && LhsType->isFloatingPoint()) + return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isInteger() && LhsType->isInteger()) + return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isFloatingPoint() && LhsType->getKind() == BuiltinType::Bool) + return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isFloatingPoint() && LhsType->isInteger()) + return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); + if (RhsType->isFloatingPoint() && LhsType->isFloatingPoint()) + return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); +} + +bool NarrowingConversionsCheck::handleConditionalOperator( + const ASTContext &Context, const Expr &Lhs, const Expr &Rhs) { + if (const auto *CO = llvm::dyn_cast<ConditionalOperator>(&Rhs)) { + // We create variables so we make sure both sides of the + // ConditionalOperator are evaluated, the || operator would short ciruit + // the second call otherwise. + handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs, + *CO->getLHS()); + handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs, + *CO->getRHS()); + return true; + } + return false; +} + +void NarrowingConversionsCheck::handleImplicitCast( + const ASTContext &Context, const ImplicitCastExpr &Cast) { + if (Cast.getExprLoc().isMacroID()) + return; + const Expr &Lhs = Cast; + const Expr &Rhs = *Cast.getSubExpr(); + if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) + return; + if (handleConditionalOperator(Context, Lhs, Rhs)) return; + SourceLocation SourceLoc = Lhs.getExprLoc(); + switch (Cast.getCastKind()) { + case CK_BooleanToSignedIntegral: + return handleBooleanToSignedIntegral(Context, SourceLoc, Lhs, Rhs); + case CK_IntegralToBoolean: + return handleIntegralToBoolean(Context, SourceLoc, Lhs, Rhs); + case CK_IntegralToFloating: + return handleIntegralToFloating(Context, SourceLoc, Lhs, Rhs); + case CK_IntegralCast: + return handleIntegralCast(Context, SourceLoc, Lhs, Rhs); + case CK_FloatingToBoolean: + return handleFloatingToBoolean(Context, SourceLoc, Lhs, Rhs); + case CK_FloatingToIntegral: + return handleFloatingToIntegral(Context, SourceLoc, Lhs, Rhs); + case CK_FloatingCast: + return handleFloatingCast(Context, SourceLoc, Lhs, Rhs); + default: + break; } - const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast"); - if (Cast->getBeginLoc().isMacroID()) +} + +void NarrowingConversionsCheck::handleBinaryOperator(const ASTContext &Context, + const BinaryOperator &Op) { + if (Op.getBeginLoc().isMacroID()) return; - diag(Cast->getExprLoc(), "narrowing conversion from %0 to %1") - << Cast->getSubExpr()->getType() << Cast->getType(); + const Expr &Lhs = *Op.getLHS(); + const Expr &Rhs = *Op.getRHS(); + if (Lhs.isInstantiationDependent() || Rhs.isInstantiationDependent()) + return; + if (handleConditionalOperator(Context, Lhs, Rhs)) + return; + handleBinaryOperator(Context, Rhs.getBeginLoc(), Lhs, Rhs); +} + +void NarrowingConversionsCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *Op = Result.Nodes.getNodeAs<BinaryOperator>("binary_op")) + return handleBinaryOperator(*Result.Context, *Op); + if (const auto *Cast = Result.Nodes.getNodeAs<ImplicitCastExpr>("cast")) + return handleImplicitCast(*Result.Context, *Cast); + llvm_unreachable("must be binary operator or cast expression"); } } // namespace cppcoreguidelines
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits