Skip to content

Commit 5bec2b7

Browse files
Added options to readability-implicit-bool-conversion (#120087)
As given in the issue #36323 , I added two new options in the clang-tools-extra/clan-tidy/readibility/ImplicitBoolConversionCheck.cpp and header file. I have also written new test cases to test these new options in test/readibility directory.
1 parent 39e8953 commit 5bec2b7

File tree

5 files changed

+183
-68
lines changed

5 files changed

+183
-68
lines changed

clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.cpp

Lines changed: 81 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,17 @@ ImplicitBoolConversionCheck::ImplicitBoolConversionCheck(
259259
AllowIntegerConditions(Options.get("AllowIntegerConditions", false)),
260260
AllowPointerConditions(Options.get("AllowPointerConditions", false)),
261261
UseUpperCaseLiteralSuffix(
262-
Options.get("UseUpperCaseLiteralSuffix", false)) {}
262+
Options.get("UseUpperCaseLiteralSuffix", false)),
263+
CheckConversionsToBool(Options.get("CheckConversionsToBool", true)),
264+
CheckConversionsFromBool(Options.get("CheckConversionsFromBool", true)) {}
263265

264266
void ImplicitBoolConversionCheck::storeOptions(
265267
ClangTidyOptions::OptionMap &Opts) {
266268
Options.store(Opts, "AllowIntegerConditions", AllowIntegerConditions);
267269
Options.store(Opts, "AllowPointerConditions", AllowPointerConditions);
268270
Options.store(Opts, "UseUpperCaseLiteralSuffix", UseUpperCaseLiteralSuffix);
271+
Options.store(Opts, "CheckConversionsToBool", CheckConversionsToBool);
272+
Options.store(Opts, "CheckConversionsFromBool", CheckConversionsFromBool);
269273
}
270274

271275
void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
@@ -277,6 +281,7 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
277281
expr(hasType(qualType().bind("type")),
278282
hasParent(initListExpr(hasParent(explicitCastExpr(
279283
hasType(qualType(equalsBoundNode("type"))))))))));
284+
280285
auto ImplicitCastFromBool = implicitCastExpr(
281286
anyOf(hasCastKind(CK_IntegralCast), hasCastKind(CK_IntegralToFloating),
282287
// Prior to C++11 cast from bool literal to pointer was allowed.
@@ -287,72 +292,84 @@ void ImplicitBoolConversionCheck::registerMatchers(MatchFinder *Finder) {
287292
auto BoolXor =
288293
binaryOperator(hasOperatorName("^"), hasLHS(ImplicitCastFromBool),
289294
hasRHS(ImplicitCastFromBool));
290-
auto ComparisonInCall = allOf(
291-
hasParent(callExpr()),
292-
hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));
293-
294295
auto IsInCompilerGeneratedFunction = hasAncestor(namedDecl(anyOf(
295296
isImplicit(), functionDecl(isDefaulted()), functionTemplateDecl())));
296297

297-
Finder->addMatcher(
298-
traverse(TK_AsIs,
299-
implicitCastExpr(
300-
anyOf(hasCastKind(CK_IntegralToBoolean),
301-
hasCastKind(CK_FloatingToBoolean),
302-
hasCastKind(CK_PointerToBoolean),
303-
hasCastKind(CK_MemberPointerToBoolean)),
304-
// Exclude cases of C23 comparison result.
305-
unless(allOf(isC23(),
306-
hasSourceExpression(ignoringParens(
307-
binaryOperator(hasAnyOperatorName(
308-
">", ">=", "==", "!=", "<", "<=")))))),
309-
// Exclude case of using if or while statements with variable
310-
// declaration, e.g.:
311-
// if (int var = functionCall()) {}
312-
unless(hasParent(
313-
stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
314-
// Exclude cases common to implicit cast to and from bool.
315-
unless(ExceptionCases), unless(has(BoolXor)),
316-
// Exclude C23 cases common to implicit cast to bool.
317-
unless(ComparisonInCall),
318-
// Retrieve also parent statement, to check if we need
319-
// additional parens in replacement.
320-
optionally(hasParent(stmt().bind("parentStmt"))),
321-
unless(isInTemplateInstantiation()),
322-
unless(IsInCompilerGeneratedFunction))
323-
.bind("implicitCastToBool")),
324-
this);
325-
326-
auto BoolComparison = binaryOperator(hasAnyOperatorName("==", "!="),
327-
hasLHS(ImplicitCastFromBool),
328-
hasRHS(ImplicitCastFromBool));
329-
auto BoolOpAssignment = binaryOperator(hasAnyOperatorName("|=", "&="),
330-
hasLHS(expr(hasType(booleanType()))));
331-
auto BitfieldAssignment = binaryOperator(
332-
hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
333-
auto BitfieldConstruct = cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
334-
withInitializer(equalsBoundNode("implicitCastFromBool")),
335-
forField(hasBitWidth(1)))));
336-
Finder->addMatcher(
337-
traverse(
338-
TK_AsIs,
339-
implicitCastExpr(
340-
ImplicitCastFromBool, unless(ExceptionCases),
341-
// Exclude comparisons of bools, as they are always cast to
342-
// integers in such context:
343-
// bool_expr_a == bool_expr_b
344-
// bool_expr_a != bool_expr_b
345-
unless(hasParent(
346-
binaryOperator(anyOf(BoolComparison, BoolXor,
347-
BoolOpAssignment, BitfieldAssignment)))),
348-
implicitCastExpr().bind("implicitCastFromBool"),
349-
unless(hasParent(BitfieldConstruct)),
350-
// Check also for nested casts, for example: bool -> int -> float.
351-
anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
352-
anything()),
353-
unless(isInTemplateInstantiation()),
354-
unless(IsInCompilerGeneratedFunction))),
355-
this);
298+
if (CheckConversionsToBool) {
299+
auto ComparisonInCall = allOf(
300+
hasParent(callExpr()),
301+
hasSourceExpression(binaryOperator(hasAnyOperatorName("==", "!="))));
302+
303+
Finder->addMatcher(
304+
traverse(
305+
TK_AsIs,
306+
implicitCastExpr(
307+
anyOf(hasCastKind(CK_IntegralToBoolean),
308+
hasCastKind(CK_FloatingToBoolean),
309+
hasCastKind(CK_PointerToBoolean),
310+
hasCastKind(CK_MemberPointerToBoolean)),
311+
// Exclude cases of C23 comparison result.
312+
unless(allOf(isC23(),
313+
hasSourceExpression(ignoringParens(
314+
binaryOperator(hasAnyOperatorName(
315+
">", ">=", "==", "!=", "<", "<=")))))),
316+
// Exclude case of using if or while statements with variable
317+
// declaration, e.g.:
318+
// if (int var = functionCall()) {}
319+
unless(hasParent(
320+
stmt(anyOf(ifStmt(), whileStmt()), has(declStmt())))),
321+
// Exclude cases common to implicit cast to and from bool.
322+
unless(ExceptionCases), unless(has(BoolXor)),
323+
// Exclude C23 cases common to implicit cast to bool.
324+
unless(ComparisonInCall),
325+
// Retrieve also parent statement, to check if we need
326+
// additional parens in replacement.
327+
optionally(hasParent(stmt().bind("parentStmt"))),
328+
unless(isInTemplateInstantiation()),
329+
unless(IsInCompilerGeneratedFunction))
330+
.bind("implicitCastToBool")),
331+
this);
332+
}
333+
334+
if (CheckConversionsFromBool) {
335+
336+
auto BoolComparison = binaryOperator(hasAnyOperatorName("==", "!="),
337+
hasLHS(ImplicitCastFromBool),
338+
hasRHS(ImplicitCastFromBool));
339+
340+
auto BoolOpAssignment = binaryOperator(
341+
hasAnyOperatorName("|=", "&="), hasLHS(expr(hasType(booleanType()))));
342+
343+
auto BitfieldAssignment = binaryOperator(
344+
hasLHS(memberExpr(hasDeclaration(fieldDecl(hasBitWidth(1))))));
345+
346+
auto BitfieldConstruct =
347+
cxxConstructorDecl(hasDescendant(cxxCtorInitializer(
348+
withInitializer(equalsBoundNode("implicitCastFromBool")),
349+
forField(hasBitWidth(1)))));
350+
351+
Finder->addMatcher(
352+
traverse(
353+
TK_AsIs,
354+
implicitCastExpr(
355+
ImplicitCastFromBool, unless(ExceptionCases),
356+
// Exclude comparisons of bools, as they are
357+
// always cast to integers in such context:
358+
// bool_expr_a == bool_expr_b
359+
// bool_expr_a != bool_expr_b
360+
unless(hasParent(binaryOperator(anyOf(BoolComparison, BoolXor,
361+
BoolOpAssignment,
362+
BitfieldAssignment)))),
363+
implicitCastExpr().bind("implicitCastFromBool"),
364+
unless(hasParent(BitfieldConstruct)),
365+
// Check also for nested casts, for example:
366+
// bool -> int -> float.
367+
anyOf(hasParent(implicitCastExpr().bind("furtherImplicitCast")),
368+
anything()),
369+
unless(isInTemplateInstantiation()),
370+
unless(IsInCompilerGeneratedFunction))),
371+
this);
372+
}
356373
}
357374

358375
void ImplicitBoolConversionCheck::check(

clang-tools-extra/clang-tidy/readability/ImplicitBoolConversionCheck.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class ImplicitBoolConversionCheck : public ClangTidyCheck {
3737
const bool AllowIntegerConditions;
3838
const bool AllowPointerConditions;
3939
const bool UseUpperCaseLiteralSuffix;
40+
const bool CheckConversionsToBool;
41+
const bool CheckConversionsFromBool;
4042
};
4143

4244
} // namespace clang::tidy::readability

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -342,10 +342,11 @@ Changes in existing checks
342342
diagnostic.
343343

344344
- Improved :doc:`readability-implicit-bool-conversion
345-
<clang-tidy/checks/readability/implicit-bool-conversion>` check
346-
by adding the option `UseUpperCaseLiteralSuffix` to select the
347-
case of the literal suffix in fixes and fixing false positive for implicit
348-
conversion of comparison result in C23.
345+
<clang-tidy/checks/readability/implicit-bool-conversion>` check by adding the
346+
option `UseUpperCaseLiteralSuffix` to select the case of the literal suffix in
347+
fixes and fixing false positive for implicit conversion of comparison result in
348+
C23, and by adding the option `CheckConversionsToBool` or
349+
`CheckConversionsFromBool` to configure checks for conversions involving ``bool``.
349350

350351
- Improved :doc:`readability-redundant-smartptr-get
351352
<clang-tidy/checks/readability/redundant-smartptr-get>` check to

clang-tools-extra/docs/clang-tidy/checks/readability/implicit-bool-conversion.rst

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,41 @@ Options
147147
if (foo) {}
148148
// ^ propose replacement default: if (foo != 0u) {}
149149
// ^ propose replacement with option `UseUpperCaseLiteralSuffix`: if (foo != 0U) {}
150+
151+
.. option:: CheckConversionsToBool
152+
153+
When `true`, the check diagnoses implicit conversions to ``bool``.
154+
Default is `true`.
155+
156+
Example
157+
158+
.. code-block:: c++
159+
160+
int x = 42;
161+
if (x) {}
162+
// ^ propose replacement: if (x != 0) {}
163+
164+
float f = 3.14;
165+
if (f) {}
166+
// ^ propose replacement: if (f != 0.0f) {}
167+
168+
.. option:: CheckConversionsFromBool
169+
170+
When `true`, the check diagnoses implicit conversions from ``bool``.
171+
Default is `true`.
172+
173+
Example
174+
175+
.. code-block:: c++
176+
177+
bool b = true;
178+
179+
int x = b;
180+
// ^ propose replacement: int x = b ? 1 : 0;
181+
182+
float f = b;
183+
// ^ propose replacement: float f = b ? 1.0f : 0.0f;
184+
185+
int* p = b;
186+
// ^ propose replacement: int* p = b ? some_ptr : nullptr;
187+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// RUN: %check_clang_tidy -check-suffix=FROM %s readability-implicit-bool-conversion %t -- \
2+
// RUN: -config='{CheckOptions: { \
3+
// RUN: readability-implicit-bool-conversion.CheckConversionsToBool: false, \
4+
// RUN: readability-implicit-bool-conversion.CheckConversionsFromBool: true \
5+
// RUN: }}' -- -std=c23
6+
// RUN: %check_clang_tidy -check-suffix=TO %s readability-implicit-bool-conversion %t -- \
7+
// RUN: -config='{CheckOptions: { \
8+
// RUN: readability-implicit-bool-conversion.CheckConversionsToBool: true, \
9+
// RUN: readability-implicit-bool-conversion.CheckConversionsFromBool: false \
10+
// RUN: }}' -- -std=c23
11+
// RUN: %check_clang_tidy -check-suffix=NORMAL %s readability-implicit-bool-conversion %t -- \
12+
// RUN: -config='{CheckOptions: { \
13+
// RUN: readability-implicit-bool-conversion.CheckConversionsToBool: false, \
14+
// RUN: readability-implicit-bool-conversion.CheckConversionsFromBool: false \
15+
// RUN: }}' -- -std=c23
16+
// RUN: %check_clang_tidy -check-suffix=TO,FROM %s readability-implicit-bool-conversion %t -- \
17+
// RUN: -config='{CheckOptions: { \
18+
// RUN: readability-implicit-bool-conversion.CheckConversionsToBool: true, \
19+
// RUN: readability-implicit-bool-conversion.CheckConversionsFromBool: true \
20+
// RUN: }}' -- -std=c23
21+
22+
// Test various implicit bool conversions in different contexts
23+
void TestImplicitBoolConversion() {
24+
// Basic type conversions to bool
25+
int intValue = 42;
26+
if (intValue) // CHECK-MESSAGES-TO: :[[@LINE]]:9: warning: implicit conversion 'int' -> 'bool' [readability-implicit-bool-conversion]
27+
// CHECK-FIXES-TO: if (intValue != 0)
28+
(void)0;
29+
30+
float floatValue = 3.14f;
31+
while (floatValue) // CHECK-MESSAGES-TO: :[[@LINE]]:12: warning: implicit conversion 'float' -> 'bool' [readability-implicit-bool-conversion]
32+
// CHECK-FIXES-TO: while (floatValue != 0.0f)
33+
break;
34+
35+
char charValue = 'a';
36+
do {
37+
break;
38+
} while (charValue); // CHECK-MESSAGES-TO: :[[@LINE]]:14: warning: implicit conversion 'char' -> 'bool' [readability-implicit-bool-conversion]
39+
// CHECK-FIXES-TO: } while (charValue != 0);
40+
41+
// Pointer conversions to bool
42+
int* ptrValue = &intValue;
43+
if (ptrValue) // CHECK-MESSAGES-TO: :[[@LINE]]:9: warning: implicit conversion 'int *' -> 'bool' [readability-implicit-bool-conversion]
44+
// CHECK-FIXES-TO: if (ptrValue != nullptr)
45+
(void)0;
46+
47+
// Conversions from bool to other types
48+
bool boolValue = true;
49+
int intFromBool = boolValue; // CHECK-MESSAGES-FROM: :[[@LINE]]:23: warning: implicit conversion 'bool' -> 'int' [readability-implicit-bool-conversion]
50+
// CHECK-FIXES-FROM: int intFromBool = static_cast<int>(boolValue);
51+
52+
float floatFromBool = boolValue; // CHECK-MESSAGES-FROM: :[[@LINE]]:27: warning: implicit conversion 'bool' -> 'float' [readability-implicit-bool-conversion]
53+
// CHECK-FIXES-FROM: float floatFromBool = static_cast<float>(boolValue);
54+
55+
char charFromBool = boolValue; // CHECK-MESSAGES-FROM: :[[@LINE]]:25: warning: implicit conversion 'bool' -> 'char' [readability-implicit-bool-conversion]
56+
// CHECK-FIXES-FROM: char charFromBool = static_cast<char>(boolValue);
57+
}

0 commit comments

Comments
 (0)