Skip to content

Commit dac74eb

Browse files
authored
fix(clang-tidy): performance fixes applied in tests and CI (#3051)
* Initial fixes * Whoops * Finish clang-tidy manual fixes * Add two missing fixes * Revert * Update clang-tidy * Try to fix unreachable code error * Move nolint comment * Apply missing fix * Don't override clang-tidy config * Does this fix clang-tidy? * Make all clang-tidy errors visible * Add comments about NOLINTs and remove a few * Fix typo
1 parent 3b30b0a commit dac74eb

36 files changed

+667
-434
lines changed

.clang-tidy

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ modernize-use-noexcept,
1818
modernize-use-emplace,
1919
modernize-use-override,
2020
modernize-use-using,
21+
*performance*,
2122
readability-container-size-empty,
2223
readability-make-member-function-const,
2324
readability-redundant-function-ptr-dereference,

.github/workflows/format.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
clang-tidy:
2828
name: Clang-Tidy
2929
runs-on: ubuntu-latest
30-
container: silkeh/clang:10
30+
container: silkeh/clang:12
3131
steps:
3232
- uses: actions/checkout@v2
3333

@@ -37,10 +37,10 @@ jobs:
3737
- name: Configure
3838
run: >
3939
cmake -S . -B build
40-
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy);--warnings-as-errors=*"
40+
-DCMAKE_CXX_CLANG_TIDY="$(which clang-tidy)"
4141
-DDOWNLOAD_EIGEN=ON
4242
-DDOWNLOAD_CATCH=ON
4343
-DCMAKE_CXX_STANDARD=17
4444
4545
- name: Build
46-
run: cmake --build build -j 2
46+
run: cmake --build build -j 2 -- --keep-going

include/pybind11/iostream.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ add_ostream_redirect(module_ m, const std::string &name = "ostream_redirect") {
262262
return class_<detail::OstreamRedirect>(std::move(m), name.c_str(), module_local())
263263
.def(init<bool, bool>(), arg("stdout") = true, arg("stderr") = true)
264264
.def("__enter__", &detail::OstreamRedirect::enter)
265-
.def("__exit__", [](detail::OstreamRedirect &self_, args) { self_.exit(); });
265+
.def("__exit__", [](detail::OstreamRedirect &self_, const args &) { self_.exit(); });
266266
}
267267

268268
PYBIND11_NAMESPACE_END(PYBIND11_NAMESPACE)

include/pybind11/pybind11.h

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1629,12 +1629,13 @@ struct enum_base {
16291629
auto static_property = handle((PyObject *) get_internals().static_property_type);
16301630

16311631
m_base.attr("__repr__") = cpp_function(
1632-
[](object arg) -> str {
1632+
[](const object &arg) -> str {
16331633
handle type = type::handle_of(arg);
16341634
object type_name = type.attr("__name__");
16351635
return pybind11::str("<{}.{}: {}>").format(type_name, enum_name(arg), int_(arg));
1636-
}, name("__repr__"), is_method(m_base)
1637-
);
1636+
},
1637+
name("__repr__"),
1638+
is_method(m_base));
16381639

16391640
m_base.attr("name") = property(cpp_function(&enum_name, name("name"), is_method(m_base)));
16401641

@@ -1718,8 +1719,10 @@ struct enum_base {
17181719
PYBIND11_ENUM_OP_CONV("__ror__", a | b);
17191720
PYBIND11_ENUM_OP_CONV("__xor__", a ^ b);
17201721
PYBIND11_ENUM_OP_CONV("__rxor__", a ^ b);
1721-
m_base.attr("__invert__") = cpp_function(
1722-
[](object arg) { return ~(int_(arg)); }, name("__invert__"), is_method(m_base));
1722+
m_base.attr("__invert__")
1723+
= cpp_function([](const object &arg) { return ~(int_(arg)); },
1724+
name("__invert__"),
1725+
is_method(m_base));
17231726
}
17241727
} else {
17251728
PYBIND11_ENUM_OP_STRICT("__eq__", int_(a).equal(int_(b)), return false);
@@ -1740,10 +1743,10 @@ struct enum_base {
17401743
#undef PYBIND11_ENUM_OP_STRICT
17411744

17421745
m_base.attr("__getstate__") = cpp_function(
1743-
[](object arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
1746+
[](const object &arg) { return int_(arg); }, name("__getstate__"), is_method(m_base));
17441747

17451748
m_base.attr("__hash__") = cpp_function(
1746-
[](object arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
1749+
[](const object &arg) { return int_(arg); }, name("__hash__"), is_method(m_base));
17471750
}
17481751

17491752
PYBIND11_NOINLINE void value(char const* name_, object value, const char *doc = nullptr) {

tests/local_bindings.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#pragma once
2+
#include <utility>
3+
24
#include "pybind11_tests.h"
35

46
/// Simple class used to test py::local:
@@ -54,7 +56,7 @@ py::class_<T> bind_local(Args && ...args) {
5456
namespace pets {
5557
class Pet {
5658
public:
57-
Pet(std::string name) : name_(name) {}
59+
Pet(std::string name) : name_(std::move(name)) {}
5860
std::string name_;
5961
const std::string &name() const { return name_; }
6062
};

tests/object.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ template <typename T> class ref {
8181
}
8282

8383
/// Move constructor
84-
ref(ref &&r) : m_ptr(r.m_ptr) {
84+
ref(ref &&r) noexcept : m_ptr(r.m_ptr) {
8585
r.m_ptr = nullptr;
8686

8787
print_move_created(this, "with pointer", m_ptr); track_move_created((ref_tag*) this);
@@ -96,7 +96,7 @@ template <typename T> class ref {
9696
}
9797

9898
/// Move another reference into the current one
99-
ref& operator=(ref&& r) {
99+
ref &operator=(ref &&r) noexcept {
100100
print_move_assigned(this, "pointer", r.m_ptr); track_move_assigned((ref_tag*) this);
101101

102102
if (*this == r)

tests/pybind11_cross_module_tests.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,8 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
104104
m.def("return_self", [](LocalVec *v) { return v; });
105105
m.def("return_copy", [](const LocalVec &v) { return LocalVec(v); });
106106

107+
// Changing this broke things with pygrep. TODO fix
108+
// NOLINTNEXTLINE
107109
class Dog : public pets::Pet { public: Dog(std::string name) : Pet(name) {}; };
108110
py::class_<pets::Pet>(m, "Pet", py::module_local())
109111
.def("name", &pets::Pet::name);
@@ -126,6 +128,7 @@ PYBIND11_MODULE(pybind11_cross_module_tests, m) {
126128
// test_missing_header_message
127129
// The main module already includes stl.h, but we need to test the error message
128130
// which appears when this header is missing.
131+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
129132
m.def("missing_header_arg", [](std::vector<float>) { });
130133
m.def("missing_header_return", []() { return std::vector<float>(); });
131134
}

tests/test_buffers.cpp

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ TEST_SUBMODULE(buffers, m) {
2727
memcpy(m_data, s.m_data, sizeof(float) * (size_t) (m_rows * m_cols));
2828
}
2929

30-
Matrix(Matrix &&s) : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
30+
Matrix(Matrix &&s) noexcept : m_rows(s.m_rows), m_cols(s.m_cols), m_data(s.m_data) {
3131
print_move_created(this);
3232
s.m_rows = 0;
3333
s.m_cols = 0;
@@ -49,7 +49,7 @@ TEST_SUBMODULE(buffers, m) {
4949
return *this;
5050
}
5151

52-
Matrix &operator=(Matrix &&s) {
52+
Matrix &operator=(Matrix &&s) noexcept {
5353
print_move_assigned(this, std::to_string(m_rows) + "x" + std::to_string(m_cols) + " matrix");
5454
if (&s != this) {
5555
delete[] m_data;
@@ -79,6 +79,7 @@ TEST_SUBMODULE(buffers, m) {
7979
py::class_<Matrix>(m, "Matrix", py::buffer_protocol())
8080
.def(py::init<py::ssize_t, py::ssize_t>())
8181
/// Construct from a buffer
82+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
8283
.def(py::init([](py::buffer const b) {
8384
py::buffer_info info = b.request();
8485
if (info.format != py::format_descriptor<float>::format() || info.ndim != 2)
@@ -89,31 +90,31 @@ TEST_SUBMODULE(buffers, m) {
8990
return v;
9091
}))
9192

92-
.def("rows", &Matrix::rows)
93-
.def("cols", &Matrix::cols)
93+
.def("rows", &Matrix::rows)
94+
.def("cols", &Matrix::cols)
9495

9596
/// Bare bones interface
96-
.def("__getitem__", [](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
97-
if (i.first >= m.rows() || i.second >= m.cols())
98-
throw py::index_error();
99-
return m(i.first, i.second);
100-
})
101-
.def("__setitem__", [](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
102-
if (i.first >= m.rows() || i.second >= m.cols())
103-
throw py::index_error();
104-
m(i.first, i.second) = v;
105-
})
106-
/// Provide buffer access
107-
.def_buffer([](Matrix &m) -> py::buffer_info {
97+
.def("__getitem__",
98+
[](const Matrix &m, std::pair<py::ssize_t, py::ssize_t> i) {
99+
if (i.first >= m.rows() || i.second >= m.cols())
100+
throw py::index_error();
101+
return m(i.first, i.second);
102+
})
103+
.def("__setitem__",
104+
[](Matrix &m, std::pair<py::ssize_t, py::ssize_t> i, float v) {
105+
if (i.first >= m.rows() || i.second >= m.cols())
106+
throw py::index_error();
107+
m(i.first, i.second) = v;
108+
})
109+
/// Provide buffer access
110+
.def_buffer([](Matrix &m) -> py::buffer_info {
108111
return py::buffer_info(
109112
m.data(), /* Pointer to buffer */
110113
{ m.rows(), m.cols() }, /* Buffer dimensions */
111114
{ sizeof(float) * size_t(m.cols()), /* Strides (in bytes) for each index */
112115
sizeof(float) }
113116
);
114-
})
115-
;
116-
117+
});
117118

118119
// test_inherited_protocol
119120
class SquareMatrix : public Matrix {
@@ -208,7 +209,5 @@ TEST_SUBMODULE(buffers, m) {
208209
})
209210
;
210211

211-
m.def("get_buffer_info", [](py::buffer buffer) {
212-
return buffer.request();
213-
});
212+
m.def("get_buffer_info", [](const py::buffer &buffer) { return buffer.request(); });
214213
}

tests/test_builtin_casters.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ class type_caster<ConstRefCasted> {
3030
// cast operator.
3131
bool load(handle, bool) { return true; }
3232

33-
operator ConstRefCasted&&() { value = {1}; return std::move(value); }
33+
operator ConstRefCasted &&() {
34+
value = {1};
35+
// NOLINTNEXTLINE(performance-move-const-arg)
36+
return std::move(value);
37+
}
3438
operator ConstRefCasted&() { value = {2}; return value; }
3539
operator ConstRefCasted*() { value = {3}; return &value; }
3640

@@ -101,6 +105,7 @@ TEST_SUBMODULE(builtin_casters, m) {
101105

102106
// test_bytes_to_string
103107
m.def("strlen", [](char *s) { return strlen(s); });
108+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
104109
m.def("string_length", [](std::string s) { return s.length(); });
105110

106111
#ifdef PYBIND11_HAS_U8STRING
@@ -146,6 +151,7 @@ TEST_SUBMODULE(builtin_casters, m) {
146151
m.def("int_passthrough_noconvert", [](int arg) { return arg; }, py::arg{}.noconvert());
147152

148153
// test_tuple
154+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
149155
m.def("pair_passthrough", [](std::pair<bool, std::string> input) {
150156
return std::make_pair(input.second, input.first);
151157
}, "Return a pair in reversed order");
@@ -177,10 +183,13 @@ TEST_SUBMODULE(builtin_casters, m) {
177183

178184
// test_none_deferred
179185
m.def("defer_none_cstring", [](char *) { return false; });
186+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
180187
m.def("defer_none_cstring", [](py::none) { return true; });
181188
m.def("defer_none_custom", [](UserType *) { return false; });
189+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
182190
m.def("defer_none_custom", [](py::none) { return true; });
183191
m.def("nodefer_none_void", [](void *) { return true; });
192+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
184193
m.def("nodefer_none_void", [](py::none) { return false; });
185194

186195
// test_void_caster
@@ -231,6 +240,7 @@ TEST_SUBMODULE(builtin_casters, m) {
231240
}, "copy"_a);
232241

233242
m.def("refwrap_iiw", [](const IncType &w) { return w.value(); });
243+
// NOLINTNEXTLINE(performance-unnecessary-value-param)
234244
m.def("refwrap_call_iiw", [](IncType &w, py::function f) {
235245
py::list l;
236246
l.append(f(std::ref(w)));

tests/test_callbacks.cpp

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ int dummy_function(int i) { return i + 1; }
1717

1818
TEST_SUBMODULE(callbacks, m) {
1919
// test_callbacks, test_function_signatures
20-
m.def("test_callback1", [](py::object func) { return func(); });
21-
m.def("test_callback2", [](py::object func) { return func("Hello", 'x', true, 5); });
20+
m.def("test_callback1", [](const py::object &func) { return func(); });
21+
m.def("test_callback2", [](const py::object &func) { return func("Hello", 'x', true, 5); });
2222
m.def("test_callback3", [](const std::function<int(int)> &func) {
2323
return "func(43) = " + std::to_string(func(43)); });
2424
m.def("test_callback4", []() -> std::function<int(int)> { return [](int i) { return i+1; }; });
@@ -27,51 +27,48 @@ TEST_SUBMODULE(callbacks, m) {
2727
});
2828

2929
// test_keyword_args_and_generalized_unpacking
30-
m.def("test_tuple_unpacking", [](py::function f) {
30+
m.def("test_tuple_unpacking", [](const py::function &f) {
3131
auto t1 = py::make_tuple(2, 3);
3232
auto t2 = py::make_tuple(5, 6);
3333
return f("positional", 1, *t1, 4, *t2);
3434
});
3535

36-
m.def("test_dict_unpacking", [](py::function f) {
36+
m.def("test_dict_unpacking", [](const py::function &f) {
3737
auto d1 = py::dict("key"_a="value", "a"_a=1);
3838
auto d2 = py::dict();
3939
auto d3 = py::dict("b"_a=2);
4040
return f("positional", 1, **d1, **d2, **d3);
4141
});
4242

43-
m.def("test_keyword_args", [](py::function f) {
44-
return f("x"_a=10, "y"_a=20);
45-
});
43+
m.def("test_keyword_args", [](const py::function &f) { return f("x"_a = 10, "y"_a = 20); });
4644

47-
m.def("test_unpacking_and_keywords1", [](py::function f) {
45+
m.def("test_unpacking_and_keywords1", [](const py::function &f) {
4846
auto args = py::make_tuple(2);
4947
auto kwargs = py::dict("d"_a=4);
5048
return f(1, *args, "c"_a=3, **kwargs);
5149
});
5250

53-
m.def("test_unpacking_and_keywords2", [](py::function f) {
51+
m.def("test_unpacking_and_keywords2", [](const py::function &f) {
5452
auto kwargs1 = py::dict("a"_a=1);
5553
auto kwargs2 = py::dict("c"_a=3, "d"_a=4);
5654
return f("positional", *py::make_tuple(1), 2, *py::make_tuple(3, 4), 5,
5755
"key"_a="value", **kwargs1, "b"_a=2, **kwargs2, "e"_a=5);
5856
});
5957

60-
m.def("test_unpacking_error1", [](py::function f) {
58+
m.def("test_unpacking_error1", [](const py::function &f) {
6159
auto kwargs = py::dict("x"_a=3);
6260
return f("x"_a=1, "y"_a=2, **kwargs); // duplicate ** after keyword
6361
});
6462

65-
m.def("test_unpacking_error2", [](py::function f) {
63+
m.def("test_unpacking_error2", [](const py::function &f) {
6664
auto kwargs = py::dict("x"_a=3);
6765
return f(**kwargs, "x"_a=1); // duplicate keyword after **
6866
});
6967

70-
m.def("test_arg_conversion_error1", [](py::function f) {
71-
f(234, UnregisteredType(), "kw"_a=567);
72-
});
68+
m.def("test_arg_conversion_error1",
69+
[](const py::function &f) { f(234, UnregisteredType(), "kw"_a = 567); });
7370

74-
m.def("test_arg_conversion_error2", [](py::function f) {
71+
m.def("test_arg_conversion_error2", [](const py::function &f) {
7572
f(234, "expected_name"_a=UnregisteredType(), "kw"_a=567);
7673
});
7774

@@ -80,7 +77,7 @@ TEST_SUBMODULE(callbacks, m) {
8077
Payload() { print_default_created(this); }
8178
~Payload() { print_destroyed(this); }
8279
Payload(const Payload &) { print_copy_created(this); }
83-
Payload(Payload &&) { print_move_created(this); }
80+
Payload(Payload &&) noexcept { print_move_created(this); }
8481
};
8582
// Export the payload constructor statistics for testing purposes:
8683
m.def("payload_cstats", &ConstructorStats::get<Payload>);
@@ -127,16 +124,17 @@ TEST_SUBMODULE(callbacks, m) {
127124
virtual ~AbstractBase() {}; // NOLINT(modernize-use-equals-default)
128125
virtual unsigned int func() = 0;
129126
};
130-
m.def("func_accepting_func_accepting_base", [](std::function<double(AbstractBase&)>) { });
127+
m.def("func_accepting_func_accepting_base",
128+
[](const std::function<double(AbstractBase &)> &) {});
131129

132130
struct MovableObject {
133131
bool valid = true;
134132

135133
MovableObject() = default;
136134
MovableObject(const MovableObject &) = default;
137135
MovableObject &operator=(const MovableObject &) = default;
138-
MovableObject(MovableObject &&o) : valid(o.valid) { o.valid = false; }
139-
MovableObject &operator=(MovableObject &&o) {
136+
MovableObject(MovableObject &&o) noexcept : valid(o.valid) { o.valid = false; }
137+
MovableObject &operator=(MovableObject &&o) noexcept {
140138
valid = o.valid;
141139
o.valid = false;
142140
return *this;
@@ -145,7 +143,7 @@ TEST_SUBMODULE(callbacks, m) {
145143
py::class_<MovableObject>(m, "MovableObject");
146144

147145
// test_movable_object
148-
m.def("callback_with_movable", [](std::function<void(MovableObject &)> f) {
146+
m.def("callback_with_movable", [](const std::function<void(MovableObject &)> &f) {
149147
auto x = MovableObject();
150148
f(x); // lvalue reference shouldn't move out object
151149
return x.valid; // must still return `true`
@@ -159,7 +157,7 @@ TEST_SUBMODULE(callbacks, m) {
159157

160158
// test async Python callbacks
161159
using callback_f = std::function<void(int)>;
162-
m.def("test_async_callback", [](callback_f f, py::list work) {
160+
m.def("test_async_callback", [](const callback_f &f, const py::list &work) {
163161
// make detached thread that calls `f` with piece of work after a little delay
164162
auto start_f = [f](int j) {
165163
auto invoke_f = [f, j] {
@@ -175,7 +173,7 @@ TEST_SUBMODULE(callbacks, m) {
175173
start_f(py::cast<int>(i));
176174
});
177175

178-
m.def("callback_num_times", [](py::function f, std::size_t num) {
176+
m.def("callback_num_times", [](const py::function &f, std::size_t num) {
179177
for (std::size_t i = 0; i < num; i++) {
180178
f();
181179
}

0 commit comments

Comments
 (0)