Skip to content

Commit f964e8a

Browse files
authored
[clang-tidy] bugprone-implicit-widening ignores const exprs that fit (#98352)
Add an option to the `bugprone-implicit-widening-of-multiplication-result` clang-tidy checker to suppress warnings when the expression is made up of all compile-time constants (literals, `constexpr` values or results, etc.) and the result of the multiplication is guaranteed to fit in both the source and destination types. This currently only works for signed integer types: * For unsigned integers, the well-defined rollover behavior on overflow prevents the checker from detecting that the expression does not actually fit in the source expr type, and would produce false negatives. I have a somewhat-hacky stab at addressing this I'd like to submit as a follow-on PR * For floating-point types, there's probably a little additional work to be done to `Expr` to calculate the result at compile time Even still, this seems like a helpful addition just for signed types, and additional types can be added.
1 parent 1301cf4 commit f964e8a

File tree

5 files changed

+83
-0
lines changed

5 files changed

+83
-0
lines changed

clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ ImplicitWideningOfMultiplicationResultCheck::
4242
UseCXXStaticCastsInCppSources(
4343
Options.get("UseCXXStaticCastsInCppSources", true)),
4444
UseCXXHeadersInCppSources(Options.get("UseCXXHeadersInCppSources", true)),
45+
IgnoreConstantIntExpr(Options.get("IgnoreConstantIntExpr", false)),
4546
IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",
4647
utils::IncludeSorter::IS_LLVM),
4748
areDiagsSelfContained()) {}
@@ -56,6 +57,7 @@ void ImplicitWideningOfMultiplicationResultCheck::storeOptions(
5657
Options.store(Opts, "UseCXXStaticCastsInCppSources",
5758
UseCXXStaticCastsInCppSources);
5859
Options.store(Opts, "UseCXXHeadersInCppSources", UseCXXHeadersInCppSources);
60+
Options.store(Opts, "IgnoreConstantIntExpr", IgnoreConstantIntExpr);
5961
Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());
6062
}
6163

@@ -84,6 +86,19 @@ void ImplicitWideningOfMultiplicationResultCheck::handleImplicitCastExpr(
8486
if (TgtWidth <= SrcWidth)
8587
return;
8688

89+
// Is the expression a compile-time constexpr that we know can fit in the
90+
// source type?
91+
if (IgnoreConstantIntExpr && ETy->isIntegerType() &&
92+
!ETy->isUnsignedIntegerType()) {
93+
if (const auto ConstExprResult = E->getIntegerConstantExpr(*Context)) {
94+
const auto TypeSize = Context->getTypeSize(ETy);
95+
llvm::APSInt WidenedResult = ConstExprResult->extOrTrunc(TypeSize);
96+
if (WidenedResult <= llvm::APSInt::getMaxValue(TypeSize, false) &&
97+
WidenedResult >= llvm::APSInt::getMinValue(TypeSize, false))
98+
return;
99+
}
100+
}
101+
87102
// Does the index expression look like it might be unintentionally computed
88103
// in a narrower-than-wanted type?
89104
const Expr *LHS = getLHSOfMulBinOp(E);

clang-tools-extra/clang-tidy/bugprone/ImplicitWideningOfMultiplicationResultCheck.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ class ImplicitWideningOfMultiplicationResultCheck : public ClangTidyCheck {
4141
private:
4242
const bool UseCXXStaticCastsInCppSources;
4343
const bool UseCXXHeadersInCppSources;
44+
const bool IgnoreConstantIntExpr;
4445
utils::IncludeInserter IncludeInserter;
4546
};
4647

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,11 @@ Changes in existing checks
245245
<clang-tidy/checks/bugprone/forwarding-reference-overload>`
246246
check to ignore deleted constructors which won't hide other overloads.
247247

248+
- Improved :doc:`bugprone-implicit-widening-of-multiplication-result
249+
<clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result>` check
250+
by adding an option to ignore constant expressions of signed integer types
251+
that fit in the source expression type.
252+
248253
- Improved :doc:`bugprone-inc-dec-in-conditions
249254
<clang-tidy/checks/bugprone/inc-dec-in-conditions>` check to ignore code
250255
within unevaluated contexts, such as ``decltype``.

clang-tools-extra/docs/clang-tidy/checks/bugprone/implicit-widening-of-multiplication-result.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ Options
4545
should ``<cstddef>`` header be suggested, or ``<stddef.h>``.
4646
Defaults to ``true``.
4747

48+
.. option:: IgnoreConstantIntExpr
49+
50+
If the multiplication operands are compile-time constants (like literals or
51+
are ``constexpr``) and fit within the source expression type, do not emit a
52+
diagnostic or suggested fix. Only considers expressions where the source
53+
expression is a signed integer type. Defaults to ``false``.
4854

4955
Examples:
5056

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// RUN: %check_clang_tidy %s bugprone-implicit-widening-of-multiplication-result %t -- \
2+
// RUN: -config='{CheckOptions: { \
3+
// RUN: bugprone-implicit-widening-of-multiplication-result.IgnoreConstantIntExpr: true \
4+
// RUN: }}' -- -target x86_64-unknown-unknown -x c++
5+
6+
long t0() {
7+
return 1 * 4;
8+
}
9+
10+
unsigned long t1() {
11+
const int a = 2;
12+
const int b = 3;
13+
return a * b;
14+
}
15+
16+
long t2() {
17+
constexpr int a = 16383; // ~1/2 of int16_t max
18+
constexpr int b = 2;
19+
return a * b;
20+
}
21+
22+
constexpr int global_value() {
23+
return 16;
24+
}
25+
26+
unsigned long t3() {
27+
constexpr int a = 3;
28+
return a * global_value();
29+
}
30+
31+
long t4() {
32+
const char a = 3;
33+
const short b = 2;
34+
const int c = 5;
35+
return c * b * a;
36+
}
37+
38+
long t5() {
39+
constexpr int min_int = (-2147483647 - 1); // A literal of -2147483648 evaluates to long
40+
return 1 * min_int;
41+
}
42+
43+
unsigned long n0() {
44+
const int a = 1073741824; // 1/2 of int32_t max
45+
const int b = 3;
46+
return a * b;
47+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: performing an implicit widening conversion to type 'unsigned long' of a multiplication performed in type 'int'
48+
// CHECK-MESSAGES: :[[@LINE-2]]:10: note: make conversion explicit to silence this warning
49+
// CHECK-MESSAGES: static_cast<unsigned long>( )
50+
// CHECK-MESSAGES: :[[@LINE-4]]:10: note: perform multiplication in a wider type
51+
}
52+
53+
double n1() {
54+
const long a = 100000000;
55+
return a * 400;
56+
}

0 commit comments

Comments
 (0)