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
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to