Skip to content

Commit c9a1c46

Browse files
committed
[clang-tidy] false positive narrowing conversion
Let's consider the following code from the issue #139467: void test(int cond, char c) { char ret = cond > 0 ? ':' : c; } Initializer of 'ret' looks the following: -ImplicitCastExpr 'char' <IntegralCast> `-ConditionalOperator 'int' |-BinaryOperator 'int' '>' | |-ImplicitCastExpr 'int' <LValueToRValue> | | `-DeclRefExpr 'int' lvalue ParmVar 'cond' 'int' | `-IntegerLiteral 'int' 0 |-CharacterLiteral 'int' 58 `-ImplicitCastExpr 'int' <IntegralCast> `-ImplicitCastExpr 'char' <LValueToRValue> `-DeclRefExpr 'char' lvalue ParmVar 'c' 'char' So it could be seen that 'RHS' of the conditional operator is DeclRefExpr 'c' which is casted to 'int' and then the whole conditional expression is casted to 'char'. But this last conversion is not narrowing, because 'RHS' was 'char' _initially_. We should just remove the cast from 'char' to 'int' before the narrowing conversion check.
1 parent 69bec0a commit c9a1c46

File tree

5 files changed

+59
-4
lines changed

5 files changed

+59
-4
lines changed

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

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -555,15 +555,22 @@ bool NarrowingConversionsCheck::handleConditionalOperator(
555555
// We have an expression like so: `output = cond ? lhs : rhs`
556556
// From the point of view of narrowing conversion we treat it as two
557557
// expressions `output = lhs` and `output = rhs`.
558-
handleBinaryOperator(Context, CO->getLHS()->getExprLoc(), Lhs,
559-
*CO->getLHS());
560-
handleBinaryOperator(Context, CO->getRHS()->getExprLoc(), Lhs,
561-
*CO->getRHS());
558+
handleConditionalOperatorArgument(Context, Lhs, CO->getLHS());
559+
handleConditionalOperatorArgument(Context, Lhs, CO->getRHS());
562560
return true;
563561
}
564562
return false;
565563
}
566564

565+
void NarrowingConversionsCheck::handleConditionalOperatorArgument(
566+
const ASTContext &Context, const Expr &Lhs, const Expr *Arg) {
567+
if (const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(Arg))
568+
if (!Arg->getIntegerConstantExpr(Context))
569+
Arg = ICE->getSubExpr();
570+
571+
handleBinaryOperator(Context, Arg->getExprLoc(), Lhs, *Arg);
572+
}
573+
567574
void NarrowingConversionsCheck::handleImplicitCast(
568575
const ASTContext &Context, const ImplicitCastExpr &Cast) {
569576
if (Cast.getExprLoc().isMacroID())

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ class NarrowingConversionsCheck : public ClangTidyCheck {
8585
bool handleConditionalOperator(const ASTContext &Context, const Expr &Lhs,
8686
const Expr &Rhs);
8787

88+
void handleConditionalOperatorArgument(const ASTContext &Context,
89+
const Expr &Lhs, const Expr *Arg);
8890
void handleImplicitCast(const ASTContext &Context,
8991
const ImplicitCastExpr &Cast);
9092

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ Changes in existing checks
134134
<clang-tidy/checks/bugprone/infinite-loop>` check by adding detection for
135135
variables introduced by structured bindings.
136136

137+
- Improved :doc:`bugprone-narrowing-conversions
138+
<clang-tidy/checks/bugprone/narrowing-conversions>` check by fixing
139+
false positive from analysis of a conditional expression in C.
140+
137141
- Improved :doc:`bugprone-reserved-identifier
138142
<clang-tidy/checks/bugprone/reserved-identifier>` check by ignoring
139143
declarations in system headers.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %check_clang_tidy %s bugprone-narrowing-conversions %t -- --
2+
3+
char test_char(int cond, char c) {
4+
char ret = cond > 0 ? ':' : c;
5+
return ret;
6+
}
7+
8+
short test_short(int cond, short s) {
9+
short ret = cond > 0 ? ':' : s;
10+
return ret;
11+
}
12+
13+
int test_int(int cond, int i) {
14+
int ret = cond > 0 ? ':' : i;
15+
return ret;
16+
}
17+
18+
void test(int cond, int i) {
19+
char x = cond > 0 ? ':' : i;
20+
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: narrowing conversion from 'int' to signed type 'char' is implementation-defined [bugprone-narrowing-conversions]
21+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %check_clang_tidy %s bugprone-narrowing-conversions %t -- --
2+
3+
char test_char(int cond, char c) {
4+
char ret = cond > 0 ? ':' : c;
5+
return ret;
6+
}
7+
8+
short test_short(int cond, short s) {
9+
short ret = cond > 0 ? ':' : s;
10+
return ret;
11+
}
12+
13+
int test_int(int cond, int i) {
14+
int ret = cond > 0 ? ':' : i;
15+
return ret;
16+
}
17+
18+
void test(int cond, int i) {
19+
char x = cond > 0 ? ':' : i;
20+
// CHECK-MESSAGES: :[[@LINE-1]]:29: warning: narrowing conversion from 'int' to signed type 'char' is implementation-defined [bugprone-narrowing-conversions]
21+
}

0 commit comments

Comments
 (0)