Skip to content

Commit e82ff98

Browse files
committed
[clang-tidy] Improve ExceptionSpecAnalyzers handling of conditional noexcept expressions
The previous code was pretty messy and treated value dependant expressions which could not be evaluated the same as if they evaluted to `false`. Which was obviously not correct. We now check if we can evaluate the dependant expressions and if not we truthfully return that we don't know if the function is declared as `noexcept` or not. This fixes #68101
1 parent f2aac19 commit e82ff98

File tree

4 files changed

+60
-14
lines changed

4 files changed

+60
-14
lines changed

clang-tools-extra/clang-tidy/utils/ExceptionSpecAnalyzer.cpp

+15-6
Original file line numberDiff line numberDiff line change
@@ -142,13 +142,22 @@ ExceptionSpecAnalyzer::analyzeFunctionEST(const FunctionDecl *FuncDecl,
142142
return State::NotThrowing;
143143
case CT_Dependent: {
144144
const Expr *NoexceptExpr = FuncProto->getNoexceptExpr();
145+
if (!NoexceptExpr)
146+
return State::NotThrowing;
147+
148+
// We can't resolve value dependence so just return unknown
149+
if (NoexceptExpr->isValueDependent())
150+
return State::Unknown;
151+
152+
// Try to evaluate the expression to a boolean value
145153
bool Result = false;
146-
return (NoexceptExpr && !NoexceptExpr->isValueDependent() &&
147-
NoexceptExpr->EvaluateAsBooleanCondition(
148-
Result, FuncDecl->getASTContext(), true) &&
149-
Result)
150-
? State::NotThrowing
151-
: State::Throwing;
154+
if (NoexceptExpr->EvaluateAsBooleanCondition(
155+
Result, FuncDecl->getASTContext(), true))
156+
return Result ? State::NotThrowing : State::Throwing;
157+
158+
// The noexcept expression is not value dependent but we can't evaluate it
159+
// as a boolean condition so we have no idea if its throwing or not
160+
return State::Unknown;
152161
}
153162
default:
154163
return State::Throwing;

clang-tools-extra/docs/ReleaseNotes.rst

+6-1
Original file line numberDiff line numberDiff line change
@@ -284,9 +284,14 @@ Changes in existing checks
284284
<clang-tidy/checks/performance/faster-string-find>` check to properly escape
285285
single quotes.
286286

287+
- Improved :doc:`performance-noexcept-move-constructor
288+
<clang-tidy/checks/performance/noexcept-move-constructor>` to better handle
289+
conditional noexcept expressions, eliminating false-positives.
290+
287291
- Improved :doc:`performance-noexcept-swap
288292
<clang-tidy/checks/performance/noexcept-swap>` check to enforce a stricter
289-
match with the swap function signature, eliminating false-positives.
293+
match with the swap function signature and better handling of condition
294+
noexcept expressions, eliminating false-positives.
290295

291296
- Improved :doc:`readability-braces-around-statements
292297
<clang-tidy/checks/readability/braces-around-statements>` check to

clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-move-constructor.cpp

+18
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
// RUN: %check_clang_tidy %s performance-noexcept-move-constructor %t -- -- -fexceptions
22

3+
namespace std
4+
{
5+
template <typename T>
6+
struct is_nothrow_move_constructible
7+
{
8+
static constexpr bool value = __is_nothrow_constructible(T, __add_rvalue_reference(T));
9+
};
10+
} // namespace std
11+
312
struct Empty
413
{};
514

@@ -379,3 +388,12 @@ struct OK31 {
379388
OK31(OK31 &&) noexcept(TrueT<int>::value) = default;
380389
OK31& operator=(OK31 &&) noexcept(TrueT<int>::value) = default;
381390
};
391+
392+
namespace gh68101
393+
{
394+
template <typename T>
395+
class Container {
396+
public:
397+
Container(Container&&) noexcept(std::is_nothrow_move_constructible<T>::value);
398+
};
399+
} // namespace gh68101

clang-tools-extra/test/clang-tidy/checkers/performance/noexcept-swap.cpp

+21-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
// RUN: %check_clang_tidy %s performance-noexcept-swap %t -- -- -fexceptions
22

3+
namespace std
4+
{
5+
template <typename T>
6+
struct is_nothrow_move_constructible
7+
{
8+
static constexpr bool value = __is_nothrow_constructible(T, __add_rvalue_reference(T));
9+
};
10+
} // namespace std
11+
312
void throwing_function() noexcept(false);
413
void noexcept_function() noexcept;
514

@@ -54,9 +63,6 @@ struct D {
5463
// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap]
5564
};
5665

57-
template <typename T>
58-
void swap(D<T> &, D<T> &) noexcept(D<T>::kFalse);
59-
// CHECK-MESSAGES: :[[@LINE-1]]:36: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap]
6066
void swap(D<int> &, D<int> &) noexcept(D<int>::kFalse);
6167
// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: noexcept specifier on swap function evaluates to 'false' [performance-noexcept-swap]
6268

@@ -151,9 +157,8 @@ struct OK16 {
151157
void swap(OK16 &) noexcept(kTrue);
152158
};
153159

154-
// FIXME: This gives a warning, but it should be OK.
155-
//template <typename T>
156-
//void swap(OK16<T> &, OK16<T> &) noexcept(OK16<T>::kTrue);
160+
template <typename T>
161+
void swap(OK16<T> &, OK16<T> &) noexcept(OK16<T>::kTrue);
157162
template <typename T>
158163
void swap(OK16<int> &, OK16<int> &) noexcept(OK16<int>::kTrue);
159164

@@ -217,4 +222,13 @@ namespace PR64303 {
217222
friend void swap(Test&, Test&);
218223
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: swap functions should be marked noexcept [performance-noexcept-swap]
219224
};
220-
}
225+
} // namespace PR64303
226+
227+
namespace gh68101
228+
{
229+
template <typename T>
230+
class Container {
231+
public:
232+
void swap(Container&) noexcept(std::is_nothrow_move_constructible<T>::value);
233+
};
234+
} // namespace gh68101

0 commit comments

Comments
 (0)