Skip to content

Commit eb3b063

Browse files
authored
[clang-tidy] Improve google-explicit-constructor checks handling of explicit(bool) (#82689)
We now treat `explicit(false)` the same way we treat `noexcept(false)` in the noexcept checks, which is ignoring it. Also introduced a new warning message if a constructor has an `explicit` declaration which evaluates to false and no longer emit a faulty FixIt. Fixes #81121
1 parent 800de14 commit eb3b063

File tree

3 files changed

+88
-8
lines changed

3 files changed

+88
-8
lines changed

clang-tools-extra/clang-tidy/google/ExplicitConstructorCheck.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,10 @@ static bool isStdInitializerList(QualType Type) {
7979
}
8080

8181
void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
82-
constexpr char WarningMessage[] =
82+
constexpr char NoExpressionWarningMessage[] =
8383
"%0 must be marked explicit to avoid unintentional implicit conversions";
84+
constexpr char WithExpressionWarningMessage[] =
85+
"%0 explicit expression evaluates to 'false'";
8486

8587
if (const auto *Conversion =
8688
Result.Nodes.getNodeAs<CXXConversionDecl>("conversion")) {
@@ -91,7 +93,7 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
9193
// gmock to define matchers).
9294
if (Loc.isMacroID())
9395
return;
94-
diag(Loc, WarningMessage)
96+
diag(Loc, NoExpressionWarningMessage)
9597
<< Conversion << FixItHint::CreateInsertion(Loc, "explicit ");
9698
return;
9799
}
@@ -101,9 +103,11 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
101103
Ctor->getMinRequiredArguments() > 1)
102104
return;
103105

106+
const ExplicitSpecifier ExplicitSpec = Ctor->getExplicitSpecifier();
107+
104108
bool TakesInitializerList = isStdInitializerList(
105109
Ctor->getParamDecl(0)->getType().getNonReferenceType());
106-
if (Ctor->isExplicit() &&
110+
if (ExplicitSpec.isExplicit() &&
107111
(Ctor->isCopyOrMoveConstructor() || TakesInitializerList)) {
108112
auto IsKwExplicit = [](const Token &Tok) {
109113
return Tok.is(tok::raw_identifier) &&
@@ -130,18 +134,31 @@ void ExplicitConstructorCheck::check(const MatchFinder::MatchResult &Result) {
130134
return;
131135
}
132136

133-
if (Ctor->isExplicit() || Ctor->isCopyOrMoveConstructor() ||
137+
if (ExplicitSpec.isExplicit() || Ctor->isCopyOrMoveConstructor() ||
134138
TakesInitializerList)
135139
return;
136140

137-
bool SingleArgument =
141+
// Don't complain about explicit(false) or dependent expressions
142+
const Expr *ExplicitExpr = ExplicitSpec.getExpr();
143+
if (ExplicitExpr) {
144+
ExplicitExpr = ExplicitExpr->IgnoreImplicit();
145+
if (isa<CXXBoolLiteralExpr>(ExplicitExpr) ||
146+
ExplicitExpr->isInstantiationDependent())
147+
return;
148+
}
149+
150+
const bool SingleArgument =
138151
Ctor->getNumParams() == 1 && !Ctor->getParamDecl(0)->isParameterPack();
139152
SourceLocation Loc = Ctor->getLocation();
140-
diag(Loc, WarningMessage)
153+
auto Diag =
154+
diag(Loc, ExplicitExpr ? WithExpressionWarningMessage
155+
: NoExpressionWarningMessage)
141156
<< (SingleArgument
142157
? "single-argument constructors"
143-
: "constructors that are callable with a single argument")
144-
<< FixItHint::CreateInsertion(Loc, "explicit ");
158+
: "constructors that are callable with a single argument");
159+
160+
if (!ExplicitExpr)
161+
Diag << FixItHint::CreateInsertion(Loc, "explicit ");
145162
}
146163

147164
} // namespace clang::tidy::google

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ Changes in existing checks
157157
<clang-tidy/checks/google/build-namespaces>` check by replacing the local
158158
option `HeaderFileExtensions` by the global option of the same name.
159159

160+
- Improved :doc:`google-explicit-constructor
161+
<clang-tidy/checks/google/explicit-constructor>` check to better handle
162+
``C++-20`` `explicit(bool)`.
163+
160164
- Improved :doc:`google-global-names-in-headers
161165
<clang-tidy/checks/google/global-names-in-headers>` check by replacing the local
162166
option `HeaderFileExtensions` by the global option of the same name.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// RUN: %check_clang_tidy %s google-explicit-constructor %t -std=c++20-or-later
2+
3+
namespace issue_81121
4+
{
5+
6+
static constexpr bool ConstFalse = false;
7+
static constexpr bool ConstTrue = true;
8+
9+
struct A {
10+
explicit(true) A(int);
11+
};
12+
13+
struct B {
14+
explicit(false) B(int);
15+
};
16+
17+
struct C {
18+
explicit(ConstTrue) C(int);
19+
};
20+
21+
struct D {
22+
explicit(ConstFalse) D(int);
23+
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: single-argument constructors explicit expression evaluates to 'false' [google-explicit-constructor]
24+
};
25+
26+
template <typename>
27+
struct E {
28+
explicit(true) E(int);
29+
};
30+
31+
template <typename>
32+
struct F {
33+
explicit(false) F(int);
34+
};
35+
36+
template <typename>
37+
struct G {
38+
explicit(ConstTrue) G(int);
39+
};
40+
41+
template <typename>
42+
struct H {
43+
explicit(ConstFalse) H(int);
44+
// CHECK-MESSAGES: :[[@LINE-1]]:24: warning: single-argument constructors explicit expression evaluates to 'false' [google-explicit-constructor]
45+
};
46+
47+
template <int Val>
48+
struct I {
49+
explicit(Val > 0) I(int);
50+
};
51+
52+
template <int Val>
53+
struct J {
54+
explicit(Val > 0) J(int);
55+
};
56+
57+
void useJ(J<0>, J<100>);
58+
59+
} // namespace issue_81121

0 commit comments

Comments
 (0)