Skip to content

Commit f9bd62f

Browse files
authored
[clang-tidy] Improve ExceptionSpecAnalyzers handling of conditional noexcept expressions (#68359)
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 1c3fdb3 commit f9bd62f

File tree

4 files changed

+60
-14
lines changed

4 files changed

+60
-14
lines changed

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

Lines changed: 15 additions & 6 deletions
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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,14 @@ Changes in existing checks
294294
<clang-tidy/checks/performance/faster-string-find>` check to properly escape
295295
single quotes.
296296

297+
- Improved :doc:`performance-noexcept-move-constructor
298+
<clang-tidy/checks/performance/noexcept-move-constructor>` to better handle
299+
conditional noexcept expressions, eliminating false-positives.
300+
297301
- Improved :doc:`performance-noexcept-swap
298302
<clang-tidy/checks/performance/noexcept-swap>` check to enforce a stricter
299-
match with the swap function signature, eliminating false-positives.
303+
match with the swap function signature and better handling of condition
304+
noexcept expressions, eliminating false-positives.
300305

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

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

Lines changed: 18 additions & 0 deletions
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

Lines changed: 21 additions & 7 deletions
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)