lebedev.ri updated this revision to Diff 334929.
lebedev.ri marked 5 inline comments as done.
lebedev.ri added a comment.
Addressed review comments.
Thanks!
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D93822/new/
https://reviews.llvm.org/D93822
Files:
clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp
clang/include/clang/AST/ASTContext.h
clang/lib/AST/ASTContext.cpp
Index: clang/lib/AST/ASTContext.cpp
===================================================================
--- clang/lib/AST/ASTContext.cpp
+++ clang/lib/AST/ASTContext.cpp
@@ -10097,7 +10097,8 @@
return getVectorType(getCorrespondingUnsignedType(VTy->getElementType()),
VTy->getNumElements(), VTy->getVectorKind());
- // For enums, we return the unsigned version of the base type.
+ // For enums, get the underlying integer type of the enum, and let the general
+ // integer type signchanging code handle it.
if (const auto *ETy = T->getAs<EnumType>())
T = ETy->getDecl()->getIntegerType();
@@ -10150,6 +10151,70 @@
}
}
+QualType ASTContext::getCorrespondingSignedType(QualType T) const {
+ assert((T->hasUnsignedIntegerRepresentation() ||
+ T->isUnsignedFixedPointType()) &&
+ "Unexpected type");
+
+ // Turn <4 x unsigned int> -> <4 x signed int>
+ if (const auto *VTy = T->getAs<VectorType>())
+ return getVectorType(getCorrespondingSignedType(VTy->getElementType()),
+ VTy->getNumElements(), VTy->getVectorKind());
+
+ // For enums, get the underlying integer type of the enum, and let the general
+ // integer type signchanging code handle it.
+ if (const auto *ETy = T->getAs<EnumType>())
+ T = ETy->getDecl()->getIntegerType();
+
+ switch (T->castAs<BuiltinType>()->getKind()) {
+ case BuiltinType::Char_U:
+ case BuiltinType::UChar:
+ return SignedCharTy;
+ case BuiltinType::UShort:
+ return ShortTy;
+ case BuiltinType::UInt:
+ return IntTy;
+ case BuiltinType::ULong:
+ return LongTy;
+ case BuiltinType::ULongLong:
+ return LongLongTy;
+ case BuiltinType::UInt128:
+ return Int128Ty;
+ // wchar_t is special. It is either unsigned or not, but when it's unsigned,
+ // there's no matching "signed wchar_t". Therefore we return the signed
+ // version of it's underlying type instead.
+ case BuiltinType::WChar_U:
+ return getSignedWCharType();
+
+ case BuiltinType::UShortAccum:
+ return ShortAccumTy;
+ case BuiltinType::UAccum:
+ return AccumTy;
+ case BuiltinType::ULongAccum:
+ return LongAccumTy;
+ case BuiltinType::SatUShortAccum:
+ return SatShortAccumTy;
+ case BuiltinType::SatUAccum:
+ return SatAccumTy;
+ case BuiltinType::SatULongAccum:
+ return SatLongAccumTy;
+ case BuiltinType::UShortFract:
+ return ShortFractTy;
+ case BuiltinType::UFract:
+ return FractTy;
+ case BuiltinType::ULongFract:
+ return LongFractTy;
+ case BuiltinType::SatUShortFract:
+ return SatShortFractTy;
+ case BuiltinType::SatUFract:
+ return SatFractTy;
+ case BuiltinType::SatULongFract:
+ return SatLongFractTy;
+ default:
+ llvm_unreachable("Unexpected unsigned integer or fixed point type");
+ }
+}
+
ASTMutationListener::~ASTMutationListener() = default;
void ASTMutationListener::DeducedReturnType(const FunctionDecl *FD,
Index: clang/include/clang/AST/ASTContext.h
===================================================================
--- clang/include/clang/AST/ASTContext.h
+++ clang/include/clang/AST/ASTContext.h
@@ -2748,6 +2748,14 @@
// a given fixed point type.
QualType getCorrespondingUnsignedType(QualType T) const;
+ // Per C99 6.2.5p6, for every signed integer type, there is a corresponding
+ // unsigned integer type. This method takes an unsigned type, and returns the
+ // corresponding signed integer type.
+ // With the introduction of fixed point types in ISO N1169, this method also
+ // accepts fixed point types and returns the corresponding signed type for
+ // a given fixed point type.
+ QualType getCorrespondingSignedType(QualType T) const;
+
// Per ISO N1169, this method accepts fixed point types and returns the
// corresponding saturated type for a given fixed point type.
QualType getCorrespondingSaturatedType(QualType Ty) const;
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-short.cpp
@@ -0,0 +1,15 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++
+
+long t0(short a, int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+long t1(short a, short b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-pointer-offset.cpp
@@ -0,0 +1,82 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++
+
+char *t0(char *base, int a, int b) {
+ return base + a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+char *t1(char *base, int a, int b) {
+ return a * b + base;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+char *t2(char *base, unsigned int a, int b) {
+ return base + a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+
+char *t3(char *base, int a, unsigned int b) {
+ return base + a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+
+char *t4(char *base, unsigned int a, unsigned int b) {
+ return base + a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+
+char *t5(char *base, int a, int b, int c) {
+ return base + a * b + c;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+char *t6(char *base, int a, int b, int c) {
+ return base + a + b * c;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:21: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:21: note: perform multiplication in a wider type
+}
+
+char *n7(char *base, int a, int b) {
+ return base + (a * b);
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:18: note: perform multiplication in a wider type
+}
+char *n8(char *base, int a, int b, int c) {
+ return base + (a * b) + c;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:18: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:18: note: perform multiplication in a wider type
+}
+char *n9(char *base, int a, int b, int c) {
+ return base + (a * b + c);
+}
+
+char *n10(char *base, int a, int b) {
+ return base + (long)(a * b);
+}
+char *n11(char *base, int a, int b) {
+ return base + (unsigned long)(a * b);
+}
+
+#ifdef __cplusplus
+template <typename T>
+char *template_test(char *base, T a, T b) {
+ return base + a * b;
+}
+char *template_test_instantiation(char *base, int a, int b) {
+ return template_test(base, a, b);
+}
+#endif
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-int.cpp
@@ -0,0 +1,97 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++
+
+long t0(int a, int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t1(int a, int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t2(unsigned int a, int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t3(unsigned int a, int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t4(int a, unsigned int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t5(int a, unsigned int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t6(unsigned int a, unsigned int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t7(unsigned int a, unsigned int b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'unsigned int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t8(int a, int b) {
+ return (a * b);
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:11: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:11: note: perform multiplication in a wider type
+}
+long t9(int a, int b) {
+ return (a)*b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+long n10(int a, int b) {
+ return (long)(a * b);
+}
+long n11(int a, int b) {
+ return (unsigned long)(a * b);
+}
+
+long n12(long a, int b) {
+ return a * b;
+}
+long n13(int a, long b) {
+ return a * b;
+}
+
+long n14(int a, int b, int c) {
+ return a + b * c;
+}
+long n15(int a, int b, int c) {
+ return a * b + c;
+}
+
+#ifdef __cplusplus
+template <typename T1, typename T2>
+T2 template_test(T1 a, T1 b) {
+ return a * b;
+}
+long template_test_instantiation(int a, int b) {
+ return template_test<int, long>(a, b);
+}
+#endif
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-char.cpp
@@ -0,0 +1,99 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++
+
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c -fsigned-char
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ -fsigned-char
+
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c -funsigned-char
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++ -funsigned-char
+
+long t0(char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t1(char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t2(unsigned char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t3(unsigned char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t4(char a, unsigned char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t5(char a, unsigned char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t6(unsigned char a, unsigned char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t7(unsigned char a, unsigned char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t8(signed char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t9(signed char a, char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t10(char a, signed char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t11(char a, signed char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+
+long t12(signed char a, signed char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
+unsigned long t13(signed char a, signed char b) {
+ return a * b;
+ // CHECK-NOTES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
+ // CHECK-NOTES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:10: note: perform multiplication in a wider type
+}
Index: clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/bugprone-implicit-widening-of-multiplication-result-array-subscript-expression.cpp
@@ -0,0 +1,73 @@
+// RUN: %check_clang_tidy -std=c99 %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c
+// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- -- -x c++
+
+char *t0(char *base, int a, int b) {
+ return &base[a * b];
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+void *t1(char *base, int a, int b) {
+ return &((a * b)[base]);
+ // CHECK-NOTES: :[[@LINE-1]]:12: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:13: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:13: note: perform multiplication in a wider type
+}
+
+char *t2(char *base, unsigned int a, int b) {
+ return &base[a * b];
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+
+char *t3(char *base, int a, unsigned int b) {
+ return &base[a * b];
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+
+char *t4(char *base, unsigned int a, unsigned int b) {
+ return &base[a * b];
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'unsigned int' is used as a pointer offset after an implicit widening conversion to type 'size_t'
+ // CHECK-NOTES: :[[@LINE-2]]:16: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:16: note: perform multiplication in a wider type
+}
+
+char *n5(char *base, int a, int b, int c) {
+ return &base[a * b + c];
+}
+char *n6(char *base, int a, int b, int c) {
+ return &base[a + b * c];
+}
+
+char *t7(char *base, int a, int b) {
+ return &base[(a * b)];
+ // CHECK-NOTES: :[[@LINE-1]]:11: warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ // CHECK-NOTES: :[[@LINE-2]]:17: note: make conversion explicit to silence this warning
+ // CHECK-NOTES: :[[@LINE-3]]:17: note: perform multiplication in a wider type
+}
+char *n8(char *base, int a, int b, int c) {
+ return &base[(a * b + c)];
+}
+char *n9(char *base, int a, int b, int c) {
+ return &base[(a * b) + c];
+}
+
+char *n10(char *base, int a, int b) {
+ return &base[(long)(a * b)];
+}
+char *n11(char *base, int a, int b) {
+ return &base[(unsigned long)(a * b)];
+}
+
+#ifdef __cplusplus
+template <typename T>
+char *template_test(char *base, T a, T b) {
+ return &base[a * b];
+}
+char *template_test_instantiation(char *base, int a, int b) {
+ return template_test(base, a, b);
+}
+#endif
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -62,6 +62,7 @@
`bugprone-fold-init-type <bugprone-fold-init-type.html>`_,
`bugprone-forward-declaration-namespace <bugprone-forward-declaration-namespace.html>`_,
`bugprone-forwarding-reference-overload <bugprone-forwarding-reference-overload.html>`_,
+ `bugprone-implicit-widening-of-multiplication-result <bugprone-implicit-widening-of-multiplication-result.html>`_, "Yes"
`bugprone-inaccurate-erase <bugprone-inaccurate-erase.html>`_, "Yes"
`bugprone-incorrect-roundings <bugprone-incorrect-roundings.html>`_,
`bugprone-infinite-loop <bugprone-infinite-loop.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.rst
@@ -0,0 +1,25 @@
+.. title:: clang-tidy - bugprone-implicit-widening-of-multiplication-result
+
+bugprone-implicit-widening-of-multiplication-result
+===================================================
+
+The check diagnoses instances where a result of a multiplication is implicitly
+widened, and suggests (with fix-it) to either silence the code by making
+widening explicit, or to perform the multiplication in a wider type,
+to avoid the widening afterwards.
+
+Examples:
+
+.. code-block:: c++
+
+ long mul(int a, int b) {
+ return a * b; // warning: performing an implicit widening conversion to type 'long' of a multiplication performed in type 'int'
+ }
+
+ char* ptr_add(char *base, int a, int b) {
+ return base + a * b; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ }
+
+ char ptr_subscript(char *base, int a, int b) {
+ return base[a * b]; // warning: result of multiplication in type 'int' is used as a pointer offset after an implicit widening conversion to type 'ssize_t'
+ }
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -77,6 +77,11 @@
New checks
^^^^^^^^^^
+- New :doc:`bugprone-implicit-widening-of-multiplication-result
+ <clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result>` check.
+
+ Diagnoses instances of an implicit widening of multiplication result.
+
- New :doc:`concurrency-thread-canceltype-asynchronous
<clang-tidy/checks/concurrency-thread-canceltype-asynchronous>` check.
Index: clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h
@@ -0,0 +1,38 @@
+//===--- ImplicitWideningOfMultiplicationResultCheck.h ----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+/// Diagnoses instances of an implicit widening of multiplication result.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone-implicit-widening-of-multiplication-result.html
+class ImplicitWideningOfMultiplicationResultCheck : public ClangTidyCheck {
+ void handleImplicitCastExpr(const ImplicitCastExpr *ICE, ASTContext *Context);
+ void handlePointerOffsetting(const Expr *E, ASTContext *Context);
+
+public:
+ ImplicitWideningOfMultiplicationResultCheck(StringRef Name,
+ ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_IMPLICITWIDENINGOFMULTIPLICATIONRESULTCHECK_H
Index: clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp
@@ -0,0 +1,192 @@
+//===--- ImplicitWideningOfMultiplicationResultCheck.cpp - clang-tidy -----===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ImplicitWideningOfMultiplicationResultCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace {
+AST_MATCHER(ImplicitCastExpr, isPartOfExplicitCast) {
+ return Node.isPartOfExplicitCast();
+}
+} // namespace
+} // namespace clang
+
+namespace clang {
+namespace tidy {
+namespace bugprone {
+
+static const Expr *getLHSOfMulBinOp(const Expr *E) {
+ assert(E == E->IgnoreParens() && "Already skipped all parens!");
+ // Is this: long r = int(x) * int(y); ?
+ // FIXME: shall we skip brackets/casts/etc?
+ const auto *BO = dyn_cast<BinaryOperator>(E);
+ if (!BO || BO->getOpcode() != BO_Mul)
+ // FIXME: what about: long r = int(x) + (int(y) * int(z)); ?
+ return nullptr;
+ return BO->getLHS()->IgnoreParens();
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
+ const ImplicitCastExpr *ICE, ASTContext *Context) {
+ const Expr *E = ICE->getSubExpr()->IgnoreParens();
+ QualType Ty = ICE->getType();
+ QualType ETy = E->getType();
+
+ assert(!ETy->isDependentType() && !Ty->isDependentType() &&
+ "Don't expect to ever get here in template Context.");
+
+ // This must be a widening cast. Else we do not care.
+ unsigned SrcWidth = Context->getIntWidth(ETy);
+ unsigned TgtWidth = Context->getIntWidth(Ty);
+ if (TgtWidth <= SrcWidth)
+ return;
+
+ // Does the index expression look like it might be unintentionally computed
+ // in a narrower-than-wanted type?
+ const Expr *LHS = getLHSOfMulBinOp(E);
+ if (!LHS)
+ return;
+
+ // Ok, looks like we should diagnose this.
+ diag(E->getBeginLoc(), "performing an implicit widening conversion to type "
+ "%0 of a multiplication performed in type %1")
+ << Ty << E->getType();
+
+ diag(E->getBeginLoc(), "make conversion explicit to silence this warning",
+ DiagnosticIDs::Note)
+ << FixItHint::CreateInsertion(E->getBeginLoc(),
+ "(" + Ty.getAsString() + ")(")
+ << FixItHint::CreateInsertion(E->getEndLoc(), ")") << E->getSourceRange();
+
+ QualType WideExprTy;
+ // Get Ty of the same signedness as ExprTy, because we only want to suggest
+ // to widen the computation, but not change it's signedness domain.
+ if (Ty->isSignedIntegerType() == ETy->isSignedIntegerType())
+ WideExprTy = Ty;
+ else if (Ty->isSignedIntegerType()) {
+ assert(ETy->isUnsignedIntegerType() &&
+ "Expected source type to be signed.");
+ WideExprTy = Context->getCorrespondingUnsignedType(Ty);
+ } else {
+ assert(Ty->isUnsignedIntegerType() &&
+ "Expected target type to be unsigned.");
+ assert(ETy->isSignedIntegerType() &&
+ "Expected source type to be unsigned.");
+ WideExprTy = Context->getCorrespondingSignedType(Ty);
+ }
+
+ diag(E->getBeginLoc(), "perform multiplication in a wider type",
+ DiagnosticIDs::Note)
+ << LHS->getSourceRange()
+ << FixItHint::CreateInsertion(E->getBeginLoc(),
+ "(" + WideExprTy.getAsString() + ")");
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::handlePointerOffsetting(
+ const Expr *E, ASTContext *Context) {
+ // We are looking for a pointer offset operation,
+ // with one hand being a pointer, and another one being an offset.
+ const Expr *PointerExpr, *IndexExpr;
+ if (const auto *BO = dyn_cast<BinaryOperator>(E)) {
+ PointerExpr = BO->getLHS();
+ IndexExpr = BO->getRHS();
+ } else if (const auto *ASE = dyn_cast<ArraySubscriptExpr>(E)) {
+ PointerExpr = ASE->getLHS();
+ IndexExpr = ASE->getRHS();
+ } else
+ return;
+
+ if (IndexExpr->getType()->isPointerType())
+ std::swap(PointerExpr, IndexExpr);
+
+ if (!PointerExpr->getType()->isPointerType() ||
+ IndexExpr->getType()->isPointerType())
+ return;
+
+ IndexExpr = IndexExpr->IgnoreParens();
+
+ QualType IndexExprType = IndexExpr->getType();
+
+ // If the index expression's type is not known (i.e. we are in a template),
+ // we can't do anything here.
+ if (IndexExprType->isDependentType())
+ return;
+
+ QualType SSizeTy = Context->getSignedSizeType();
+ QualType USizeTy = Context->getSizeType();
+ QualType SizeTy = IndexExprType->isSignedIntegerType() ? SSizeTy : USizeTy;
+ // FIXME: is there a way to actually get the QualType for size_t/ssize_t?
+ // Note that SizeTy.getAsString() will be long/unsigned long/..., NOT size_t!
+ StringRef TyAsString =
+ IndexExprType->isSignedIntegerType() ? "ssize_t" : "size_t";
+
+ // So, is size_t actually wider than the result of the multiplication?
+ if (Context->getIntWidth(IndexExprType) >= Context->getIntWidth(SizeTy))
+ return;
+
+ // Does the index expression look like it might be unintentionally computed
+ // in a narrower-than-wanted type?
+ const Expr *LHS = getLHSOfMulBinOp(IndexExpr);
+ if (!LHS)
+ return;
+
+ // Ok, looks like we should diagnose this.
+ diag(E->getBeginLoc(),
+ "result of multiplication in type %0 is used as a pointer offset after "
+ "an implicit widening conversion to type '%1'")
+ << IndexExprType << TyAsString;
+
+ diag(IndexExpr->getBeginLoc(),
+ "make conversion explicit to silence this warning", DiagnosticIDs::Note)
+ << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
+ (Twine("(") + TyAsString + ")(").str())
+ << FixItHint::CreateInsertion(IndexExpr->getEndLoc(), ")")
+ << IndexExpr->getSourceRange();
+
+ diag(IndexExpr->getBeginLoc(), "perform multiplication in a wider type",
+ DiagnosticIDs::Note)
+ << LHS->getSourceRange()
+ << FixItHint::CreateInsertion(IndexExpr->getBeginLoc(),
+ (Twine("(") + TyAsString + ")").str());
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::registerMatchers(
+ MatchFinder *Finder) {
+ Finder->addMatcher(implicitCastExpr(unless(anyOf(isInTemplateInstantiation(),
+ isPartOfExplicitCast())),
+ hasCastKind(CK_IntegralCast))
+ .bind("x"),
+ this);
+ Finder->addMatcher(
+ arraySubscriptExpr(unless(isInTemplateInstantiation())).bind("x"), this);
+ Finder->addMatcher(binaryOperator(unless(isInTemplateInstantiation()),
+ hasType(isAnyPointer()),
+ hasAnyOperatorName("+", "-", "+=", "-="))
+ .bind("x"),
+ this);
+}
+
+void ImplicitWideningOfMultiplicationResultCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *MatchedDecl = Result.Nodes.getNodeAs<ImplicitCastExpr>("x"))
+ handleImplicitCastExpr(MatchedDecl, Result.Context);
+ else if (const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<ArraySubscriptExpr>("x"))
+ handlePointerOffsetting(MatchedDecl, Result.Context);
+ else if (const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<BinaryOperator>("x"))
+ handlePointerOffsetting(MatchedDecl, Result.Context);
+}
+
+} // namespace bugprone
+} // namespace tidy
+} // namespace clang
Index: clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt
@@ -17,6 +17,7 @@
FoldInitTypeCheck.cpp
ForwardDeclarationNamespaceCheck.cpp
ForwardingReferenceOverloadCheck.cpp
+ ImplicitWideningOfMultiplicationResultCheck.cpp
InaccurateEraseCheck.cpp
IncorrectRoundingsCheck.cpp
InfiniteLoopCheck.cpp
Index: clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
+++ clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp
@@ -22,6 +22,7 @@
#include "FoldInitTypeCheck.h"
#include "ForwardDeclarationNamespaceCheck.h"
#include "ForwardingReferenceOverloadCheck.h"
+#include "ImplicitWideningOfMultiplicationResultCheck.h"
#include "InaccurateEraseCheck.h"
#include "IncorrectRoundingsCheck.h"
#include "InfiniteLoopCheck.h"
@@ -97,6 +98,8 @@
"bugprone-forward-declaration-namespace");
CheckFactories.registerCheck<ForwardingReferenceOverloadCheck>(
"bugprone-forwarding-reference-overload");
+ CheckFactories.registerCheck<ImplicitWideningOfMultiplicationResultCheck>(
+ "bugprone-implicit-widening-of-multiplication-result");
CheckFactories.registerCheck<InaccurateEraseCheck>(
"bugprone-inaccurate-erase");
CheckFactories.registerCheck<IncorrectRoundingsCheck>(
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits