@pilllesss/yorn 1.0.182 → 1.0.183

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +1 -1
  2. package/dist/providers/data/.manifest.json +1 -1
  3. package/dist/skills/code-review/LICENSE +21 -0
  4. package/dist/skills/code-review/SKILL.md +233 -0
  5. package/dist/skills/code-review/assets/pr-review-template.md +137 -0
  6. package/dist/skills/code-review/assets/review-checklist.md +123 -0
  7. package/dist/skills/code-review/reference/angular.md +768 -0
  8. package/dist/skills/code-review/reference/architecture-review-guide.md +472 -0
  9. package/dist/skills/code-review/reference/c.md +890 -0
  10. package/dist/skills/code-review/reference/code-quality-universal.md +488 -0
  11. package/dist/skills/code-review/reference/code-review-best-practices.md +136 -0
  12. package/dist/skills/code-review/reference/common-bugs-checklist.md +302 -0
  13. package/dist/skills/code-review/reference/cpp.md +893 -0
  14. package/dist/skills/code-review/reference/cross-cutting/async-concurrency-patterns.md +515 -0
  15. package/dist/skills/code-review/reference/cross-cutting/error-handling-principles.md +492 -0
  16. package/dist/skills/code-review/reference/cross-cutting/n-plus-one-queries.md +309 -0
  17. package/dist/skills/code-review/reference/cross-cutting/sql-injection-prevention.md +308 -0
  18. package/dist/skills/code-review/reference/cross-cutting/xss-prevention.md +264 -0
  19. package/dist/skills/code-review/reference/csharp.md +525 -0
  20. package/dist/skills/code-review/reference/css-less-sass.md +661 -0
  21. package/dist/skills/code-review/reference/dart.md +670 -0
  22. package/dist/skills/code-review/reference/django.md +985 -0
  23. package/dist/skills/code-review/reference/fastapi.md +580 -0
  24. package/dist/skills/code-review/reference/go.md +993 -0
  25. package/dist/skills/code-review/reference/java.md +409 -0
  26. package/dist/skills/code-review/reference/java8.md +586 -0
  27. package/dist/skills/code-review/reference/kotlin.md +1018 -0
  28. package/dist/skills/code-review/reference/nestjs.md +593 -0
  29. package/dist/skills/code-review/reference/performance-review-guide.md +816 -0
  30. package/dist/skills/code-review/reference/php.md +684 -0
  31. package/dist/skills/code-review/reference/python.md +1073 -0
  32. package/dist/skills/code-review/reference/qt.md +757 -0
  33. package/dist/skills/code-review/reference/react.md +871 -0
  34. package/dist/skills/code-review/reference/ruby.md +964 -0
  35. package/dist/skills/code-review/reference/rust.md +846 -0
  36. package/dist/skills/code-review/reference/security-review-guide.md +494 -0
  37. package/dist/skills/code-review/reference/svelte.md +1064 -0
  38. package/dist/skills/code-review/reference/swift.md +936 -0
  39. package/dist/skills/code-review/reference/typescript.md +1016 -0
  40. package/dist/skills/code-review/reference/vue.md +924 -0
  41. package/dist/skills/code-review/reference/zig.md +440 -0
  42. package/dist/skills/code-review/scripts/pr-analyzer.py +435 -0
  43. package/dist/skills/code-review/scripts/test_pr_analyzer.py +380 -0
  44. package/dist/yorn.cjs +628 -628
  45. package/package.json +2 -2
@@ -0,0 +1,893 @@
1
+ # C++ Code Review Guide
2
+
3
+ > C++ code review guide focused on memory safety, lifetime, API design, modern features, and performance. Examples assume C++17/20/23.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Ownership and RAII](#ownership-and-raii)
8
+ - [Smart Pointer Selection Guide](#smart-pointer-selection-guide)
9
+ - [Lifetime and References](#lifetime-and-references)
10
+ - [Copy and Move Semantics](#copy-and-move-semantics)
11
+ - [Const-Correctness and API Design](#const-correctness-and-api-design)
12
+ - [Error Handling and Exception Safety](#error-handling-and-exception-safety)
13
+ - [Modern C++20/23 Features](#modern-c2023-features)
14
+ - [constexpr and consteval](#constexpr-and-consteval)
15
+ - [Concurrency](#concurrency)
16
+ - [Performance and Allocation](#performance-and-allocation)
17
+ - [Templates and Type Safety](#templates-and-type-safety)
18
+ - [Testing](#testing)
19
+ - [Tooling and Build Checks](#tooling-and-build-checks)
20
+ - [Review Checklist](#review-checklist)
21
+
22
+ ---
23
+
24
+ ## Ownership and RAII
25
+
26
+ ### Prefer RAII and smart pointers
27
+
28
+ Use RAII to express ownership. Default to `std::unique_ptr`, use `std::shared_ptr` only for shared lifetime.
29
+
30
+ ```cpp
31
+ // ❌ Bad: manual new/delete with early returns
32
+ Foo* make_foo() {
33
+ Foo* foo = new Foo();
34
+ if (!foo->Init()) {
35
+ delete foo;
36
+ return nullptr;
37
+ }
38
+ return foo;
39
+ }
40
+
41
+ // ✅ Good: RAII with unique_ptr
42
+ std::unique_ptr<Foo> make_foo() {
43
+ auto foo = std::make_unique<Foo>();
44
+ if (!foo->Init()) {
45
+ return {};
46
+ }
47
+ return foo;
48
+ }
49
+ ```
50
+
51
+ ### Wrap C resources
52
+
53
+ ```cpp
54
+ // ✅ Good: wrap FILE* with unique_ptr
55
+ using FilePtr = std::unique_ptr<FILE, decltype(&fclose)>;
56
+
57
+ FilePtr open_file(const char* path) {
58
+ return FilePtr(fopen(path, "rb"), &fclose);
59
+ }
60
+ ```
61
+
62
+ ### RAII best practices
63
+
64
+ ```cpp
65
+ // ✅ Good: RAII wrapper for POSIX file descriptors
66
+ class Fd {
67
+ int fd_ = -1;
68
+ public:
69
+ explicit Fd(int fd) : fd_(fd) {}
70
+ ~Fd() { if (fd_ >= 0) ::close(fd_); }
71
+
72
+ Fd(const Fd&) = delete;
73
+ Fd& operator=(const Fd&) = delete;
74
+ Fd(Fd&& o) noexcept : fd_(std::exchange(o.fd_, -1)) {}
75
+ Fd& operator=(Fd&& o) noexcept {
76
+ if (this != &o) {
77
+ if (fd_ >= 0) ::close(fd_);
78
+ fd_ = std::exchange(o.fd_, -1);
79
+ }
80
+ return *this;
81
+ }
82
+
83
+ int get() const { return fd_; }
84
+ int release() { return std::exchange(fd_, -1); }
85
+ };
86
+ ```
87
+
88
+ ### Never mix ownership styles
89
+
90
+ ```cpp
91
+ // ❌ Bad: raw new + container of raw pointers — who deletes?
92
+ std::vector<Widget*> widgets;
93
+ widgets.push_back(new Widget());
94
+ // When is delete called? Unclear.
95
+
96
+ // ✅ Good: container of unique_ptr
97
+ std::vector<std::unique_ptr<Widget>> widgets;
98
+ widgets.push_back(std::make_unique<Widget>());
99
+ // Automatically cleaned up when vector is destroyed.
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Smart Pointer Selection Guide
105
+
106
+ ### Decision matrix
107
+
108
+ | Scenario | Pointer | Why |
109
+ |----------|---------|-----|
110
+ | Single owner | `unique_ptr` | Zero overhead, clear ownership |
111
+ | Shared ownership (few owners) | `shared_ptr` | Reference counted, thread-safe refcount |
112
+ | Non-owning observer | `weak_ptr` | Breaks cycles, checks liveness |
113
+ | Never-null reference | raw reference `T&` | No ownership, cannot be null |
114
+ | Maybe-null observer | raw pointer `T*` | No ownership, can be null |
115
+
116
+ ### Avoid shared_ptr when unique_ptr suffices
117
+
118
+ ```cpp
119
+ // ❌ Bad: unnecessary shared ownership
120
+ class Window {
121
+ std::shared_ptr<Renderer> renderer_;
122
+ public:
123
+ Window() : renderer_(std::make_shared<Renderer>()) {}
124
+ };
125
+
126
+ // ✅ Good: sole owner uses unique_ptr
127
+ class Window {
128
+ std::unique_ptr<Renderer> renderer_;
129
+ public:
130
+ Window() : renderer_(std::make_unique<Renderer>()) {}
131
+ };
132
+ ```
133
+
134
+ ### Break cycles with weak_ptr
135
+
136
+ ```cpp
137
+ // ❌ Bad: cycle → memory leak
138
+ struct Node {
139
+ std::shared_ptr<Node> parent;
140
+ std::shared_ptr<Node> child;
141
+ };
142
+
143
+ // ✅ Good: weak_ptr breaks the back-reference
144
+ struct Node {
145
+ std::weak_ptr<Node> parent; // non-owning back-reference
146
+ std::shared_ptr<Node> child; // owning forward-reference
147
+ };
148
+ ```
149
+
150
+ ---
151
+
152
+ ## Lifetime and References
153
+
154
+ ### Avoid dangling references and views
155
+
156
+ `std::string_view` and `std::span` do not own data. Make sure the owner outlives the view.
157
+
158
+ ```cpp
159
+ // ❌ Bad: returning string_view to a temporary
160
+ std::string_view bad_view() {
161
+ std::string s = make_name();
162
+ return s; // dangling
163
+ }
164
+
165
+ // ✅ Good: return owning string
166
+ std::string good_name() {
167
+ return make_name();
168
+ }
169
+
170
+ // ✅ Good: view tied to caller-owned data
171
+ std::string_view good_view(const std::string& s) {
172
+ return s;
173
+ }
174
+ ```
175
+
176
+ ### Lambda captures
177
+
178
+ ```cpp
179
+ // ❌ Bad: capture reference that escapes
180
+ std::function<void()> make_task() {
181
+ int value = 42;
182
+ return [&]() { use(value); }; // dangling
183
+ }
184
+
185
+ // ✅ Good: capture by value
186
+ std::function<void()> make_task() {
187
+ int value = 42;
188
+ return [value]() { use(value); };
189
+ }
190
+ ```
191
+
192
+ ### Beware of temporary lifetime extension pitfalls
193
+
194
+ ```cpp
195
+ // ❌ Bad: reference bound to temporary that is destroyed
196
+ const std::string& name = get_name(); // temporary destroyed at end of statement
197
+ use(name); // dangling reference
198
+
199
+ // ✅ Good: store the value
200
+ std::string name = get_name();
201
+ use(name);
202
+ ```
203
+
204
+ ---
205
+
206
+ ## Copy and Move Semantics
207
+
208
+ ### Rule of 0/3/5
209
+
210
+ Prefer the Rule of 0 by using RAII types. If you own a resource, define or delete copy and move operations.
211
+
212
+ ```cpp
213
+ // ❌ Bad: raw ownership with default copy
214
+ struct Buffer {
215
+ int* data;
216
+ size_t size;
217
+ explicit Buffer(size_t n) : data(new int[n]), size(n) {}
218
+ ~Buffer() { delete[] data; }
219
+ // copy ctor/assign are implicitly generated -> double delete
220
+ };
221
+
222
+ // ✅ Good: Rule of 0 with std::vector
223
+ struct Buffer {
224
+ std::vector<int> data;
225
+ explicit Buffer(size_t n) : data(n) {}
226
+ };
227
+ ```
228
+
229
+ ### Delete unwanted copies
230
+
231
+ ```cpp
232
+ struct Socket {
233
+ Socket() = default;
234
+ ~Socket() { close(); }
235
+
236
+ Socket(const Socket&) = delete;
237
+ Socket& operator=(const Socket&) = delete;
238
+ Socket(Socket&&) noexcept = default;
239
+ Socket& operator=(Socket&&) noexcept = default;
240
+ };
241
+ ```
242
+
243
+ ### Use std::move explicitly
244
+
245
+ ```cpp
246
+ // ❌ Bad: copies instead of moves
247
+ std::string name = get_name();
248
+ data_.push_back(name); // copy
249
+
250
+ // ✅ Good: move when source is no longer needed
251
+ std::string name = get_name();
252
+ data_.push_back(std::move(name));
253
+ ```
254
+
255
+ ---
256
+
257
+ ## Const-Correctness and API Design
258
+
259
+ ### Use const and explicit
260
+
261
+ ```cpp
262
+ class User {
263
+ public:
264
+ const std::string& name() const { return name_; }
265
+ void set_name(std::string name) { name_ = std::move(name); }
266
+
267
+ private:
268
+ std::string name_;
269
+ };
270
+
271
+ struct Millis {
272
+ explicit Millis(int v) : value(v) {}
273
+ int value;
274
+ };
275
+ ```
276
+
277
+ ### Avoid object slicing
278
+
279
+ ```cpp
280
+ struct Shape { virtual ~Shape() = default; };
281
+ struct Circle : Shape { void draw() const; };
282
+
283
+ // ❌ Bad: slices Circle into Shape
284
+ void draw(Shape shape);
285
+
286
+ // ✅ Good: pass by reference
287
+ void draw(const Shape& shape);
288
+ ```
289
+
290
+ ### Use override and final
291
+
292
+ ```cpp
293
+ struct Base {
294
+ virtual void run() = 0;
295
+ };
296
+
297
+ struct Worker final : Base {
298
+ void run() override {}
299
+ };
300
+ ```
301
+
302
+ ---
303
+
304
+ ## Error Handling and Exception Safety
305
+
306
+ ### Prefer RAII for cleanup
307
+
308
+ ```cpp
309
+ // ✅ Good: RAII handles cleanup on exceptions
310
+ void process() {
311
+ std::vector<int> data = load_data(); // safe cleanup
312
+ do_work(data);
313
+ }
314
+ ```
315
+
316
+ ### Do not throw from destructors
317
+
318
+ ```cpp
319
+ struct File {
320
+ ~File() noexcept { close(); }
321
+ void close();
322
+ };
323
+ ```
324
+
325
+ ### Use expected results for normal failures
326
+
327
+ ```cpp
328
+ // ✅ C++23: std::expected
329
+ #include <expected>
330
+
331
+ std::expected<int, ParseError> parse_int(std::string_view s) {
332
+ try {
333
+ return std::stoi(std::string(s));
334
+ } catch (const std::invalid_argument&) {
335
+ return std::unexpected(ParseError::InvalidFormat);
336
+ } catch (const std::out_of_range&) {
337
+ return std::unexpected(ParseError::OutOfRange);
338
+ }
339
+ }
340
+
341
+ // ✅ Pre-C++23: std::optional
342
+ std::optional<int> parse_int(const std::string& s) {
343
+ try {
344
+ return std::stoi(s);
345
+ } catch (...) {
346
+ return std::nullopt;
347
+ }
348
+ }
349
+ ```
350
+
351
+ ### Exception safety levels
352
+
353
+ - **No-throw guarantee**: `noexcept` — destructors, swap, move operations.
354
+ - **Strong guarantee**: operation either succeeds or state is unchanged. Use copy-and-swap idiom.
355
+ - **Basic guarantee**: on exception, no resources leaked, object in valid (but possibly modified) state.
356
+
357
+ ```cpp
358
+ // ✅ Good: strong guarantee via copy-and-swap
359
+ void Container::push_back(const Item& item) {
360
+ Container tmp(*this); // copy
361
+ tmp.push_back_impl(item); // may throw, but tmp is a copy
362
+ swap(*this, tmp); // noexcept swap
363
+ }
364
+ ```
365
+
366
+ ---
367
+
368
+ ## Modern C++20/23 Features
369
+
370
+ ### Concepts (C++20)
371
+
372
+ ```cpp
373
+ // ❌ Bad: SFINAE boilerplate
374
+ template <typename T, std::enable_if_t<std::is_integral_v<T>, int> = 0>
375
+ T gcd(T a, T b) {
376
+ while (b) { a %= b; std::swap(a, b); }
377
+ return a;
378
+ }
379
+
380
+ // ✅ Good: concepts are readable and composable
381
+ template <std::integral T>
382
+ T gcd(T a, T b) {
383
+ while (b) { a %= b; std::swap(a, b); }
384
+ return a;
385
+ }
386
+
387
+ // ✅ Define custom concepts
388
+ template <typename T>
389
+ concept Printable = requires(T t, std::ostream& os) {
390
+ { os << t } -> std::convertible_to<std::ostream&>;
391
+ };
392
+
393
+ void log(const Printable auto& value) {
394
+ std::cout << "[LOG] " << value << '\n';
395
+ }
396
+ ```
397
+
398
+ ### Ranges and views (C++20)
399
+
400
+ ```cpp
401
+ #include <ranges>
402
+ #include <vector>
403
+ #include <numeric>
404
+
405
+ // ✅ Good: composable range pipelines
406
+ std::vector<int> scores = {85, 92, 67, 73, 98, 55};
407
+
408
+ auto top_scores = scores
409
+ | std::views::filter([](int s) { return s >= 80; })
410
+ | std::views::transform([](int s) { return s * 1.1; }) // bonus
411
+ | std::views::take(3);
412
+
413
+ // Iterate without allocating intermediate containers
414
+ for (double s : top_scores) {
415
+ std::cout << s << ' ';
416
+ }
417
+ ```
418
+
419
+ ### Modules (C++20)
420
+
421
+ ```cpp
422
+ // ✅ Module interface unit (math.cppm)
423
+ export module math;
424
+
425
+ export int add(int a, int b) { return a + b; }
426
+ export constexpr double pi = 3.14159265358979;
427
+
428
+ // ✅ Module implementation unit (math_impl.cpp)
429
+ module math;
430
+
431
+ int internal_helper() { /* not exported */ }
432
+
433
+ // Consumer:
434
+ import math;
435
+ int result = add(1, 2);
436
+ ```
437
+
438
+ **Review note**: Modules are still maturing in tooling support. Check that your build system (CMake 3.28+, MSVC 17.x, Clang 16+) supports them before adopting. Headers remain the safe default.
439
+
440
+ ### Deducing this (C++23)
441
+
442
+ ```cpp
443
+ // ❌ Bad: verbose CRTP for static polymorphism
444
+ template <typename Derived>
445
+ struct Base {
446
+ void call() { static_cast<Derived*>(this)->impl(); }
447
+ };
448
+
449
+ // ✅ Good: C++23 explicit object parameter
450
+ struct Widget {
451
+ template <typename Self>
452
+ void log(this Self&& self) {
453
+ // self is Widget& or Widget&& depending on call context
454
+ std::cout << self.name << '\n';
455
+ }
456
+ std::string name;
457
+ };
458
+ ```
459
+
460
+ ---
461
+
462
+ ## constexpr and consteval
463
+
464
+ ### When to use constexpr vs consteval
465
+
466
+ - `constexpr`: can be evaluated at compile time *or* runtime.
467
+ - `consteval`: **must** be evaluated at compile time (immediate function).
468
+
469
+ ```cpp
470
+ // ✅ constexpr: compile-time when possible, runtime otherwise
471
+ constexpr int factorial(int n) {
472
+ int result = 1;
473
+ for (int i = 2; i <= n; ++i) result *= i;
474
+ return result;
475
+ }
476
+
477
+ constexpr int c = factorial(5); // compile-time
478
+ int r = factorial(argc); // runtime
479
+
480
+ // ✅ consteval: enforce compile-time evaluation
481
+ consteval int forced_compiletime(int n) {
482
+ return n * n;
483
+ }
484
+
485
+ constexpr int v = forced_compiletime(42); // OK
486
+ // int v2 = forced_compiletime(argc); // ERROR: not a constant expression
487
+ ```
488
+
489
+ ### Compile-time computation for performance
490
+
491
+ ```cpp
492
+ // ✅ Good: lookup table generated at compile time
493
+ constexpr auto make_crc_table() {
494
+ std::array<uint32_t, 256> table{};
495
+ for (uint32_t i = 0; i < 256; ++i) {
496
+ uint32_t crc = i;
497
+ for (int j = 0; j < 8; ++j) {
498
+ crc = (crc >> 1) ^ (crc & 1 ? 0xEDB88320 : 0);
499
+ }
500
+ table[i] = crc;
501
+ }
502
+ return table;
503
+ }
504
+
505
+ static constexpr auto crc_table = make_crc_table();
506
+
507
+ // Use at runtime with zero initialization cost
508
+ uint32_t crc32(const uint8_t* data, size_t len) {
509
+ uint32_t crc = 0xFFFFFFFF;
510
+ for (size_t i = 0; i < len; ++i) {
511
+ crc = (crc >> 8) ^ crc_table[(crc ^ data[i]) & 0xFF];
512
+ }
513
+ return ~crc;
514
+ }
515
+ ```
516
+
517
+ ### constinit for guaranteed static initialization
518
+
519
+ ```cpp
520
+ // ✅ Good: prevent static initialization order fiasco
521
+ constinit int global_counter = 0; // guaranteed static init, not dynamic
522
+ ```
523
+
524
+ ---
525
+
526
+ ## Concurrency
527
+
528
+ ### Protect shared data
529
+
530
+ ```cpp
531
+ // ❌ Bad: data race
532
+ int counter = 0;
533
+ void inc() { counter++; }
534
+
535
+ // ✅ Good: atomic
536
+ std::atomic<int> counter{0};
537
+ void inc() { counter.fetch_add(1, std::memory_order_relaxed); }
538
+ ```
539
+
540
+ ### Use RAII locks
541
+
542
+ ```cpp
543
+ std::mutex mu;
544
+ std::vector<int> data;
545
+
546
+ void add(int v) {
547
+ std::lock_guard<std::mutex> lock(mu);
548
+ data.push_back(v);
549
+ }
550
+ ```
551
+
552
+ ### Prefer std::jthread over std::thread (C++20)
553
+
554
+ ```cpp
555
+ // ❌ Bad: std::thread requires manual join
556
+ void run() {
557
+ std::thread t([]{ do_work(); });
558
+ // forgot to join → std::terminate
559
+ }
560
+
561
+ // ✅ Good: jthread joins automatically on destruction
562
+ void run() {
563
+ std::jthread t([](std::stop_token st) {
564
+ while (!st.stop_requested()) {
565
+ do_work();
566
+ }
567
+ });
568
+ // automatically joined; stop token enables cooperative cancellation
569
+ }
570
+ ```
571
+
572
+ ### Structured concurrency with std::execution (future C++26)
573
+
574
+ Note: As of C++23, use `std::jthread` + `std::stop_token` for cooperative cancellation. The `std::execution` library (P2300) is expected in C++26.
575
+
576
+ ---
577
+
578
+ ## Performance and Allocation
579
+
580
+ ### Avoid repeated allocations
581
+
582
+ ```cpp
583
+ // ❌ Bad: repeated reallocation
584
+ std::vector<int> build(int n) {
585
+ std::vector<int> out;
586
+ for (int i = 0; i < n; ++i) {
587
+ out.push_back(i);
588
+ }
589
+ return out;
590
+ }
591
+
592
+ // ✅ Good: reserve upfront
593
+ std::vector<int> build(int n) {
594
+ std::vector<int> out;
595
+ out.reserve(static_cast<size_t>(n));
596
+ for (int i = 0; i < n; ++i) {
597
+ out.push_back(i);
598
+ }
599
+ return out;
600
+ }
601
+ ```
602
+
603
+ ### String concatenation
604
+
605
+ ```cpp
606
+ // ❌ Bad: repeated allocation
607
+ std::string join(const std::vector<std::string>& parts) {
608
+ std::string out;
609
+ for (const auto& p : parts) {
610
+ out += p;
611
+ }
612
+ return out;
613
+ }
614
+
615
+ // ✅ Good: reserve total size
616
+ std::string join(const std::vector<std::string>& parts) {
617
+ size_t total = 0;
618
+ for (const auto& p : parts) {
619
+ total += p.size();
620
+ }
621
+ std::string out;
622
+ out.reserve(total);
623
+ for (const auto& p : parts) {
624
+ out += p;
625
+ }
626
+ return out;
627
+ }
628
+ ```
629
+
630
+ ### Small Buffer Optimization (SBO)
631
+
632
+ ```cpp
633
+ // ✅ Good: avoid heap for small data
634
+ void process(const char* name) {
635
+ // Use stack for short names, heap only for long ones
636
+ std::string buf;
637
+ buf.reserve(64); // typically stays on stack via SSO
638
+ buf = name;
639
+ // ...
640
+ }
641
+ ```
642
+
643
+ ### Use std::span for zero-copy views
644
+
645
+ ```cpp
646
+ // ❌ Bad: copies the vector
647
+ void process(std::vector<int> data);
648
+
649
+ // ✅ Good: non-owning view, works with vector, array, C array
650
+ void process(std::span<const int> data);
651
+
652
+ std::vector<int> v = {1, 2, 3};
653
+ process(v); // no copy
654
+ int arr[] = {4, 5, 6};
655
+ process(arr); // no copy
656
+ ```
657
+
658
+ ---
659
+
660
+ ## Templates and Type Safety
661
+
662
+ ### Prefer constrained templates (C++20)
663
+
664
+ ```cpp
665
+ // ❌ Bad: overly generic
666
+ template <typename T>
667
+ T add(T a, T b) {
668
+ return a + b;
669
+ }
670
+
671
+ // ✅ Good: constrained
672
+ template <typename T>
673
+ requires std::is_integral_v<T>
674
+ T add(T a, T b) {
675
+ return a + b;
676
+ }
677
+ ```
678
+
679
+ ### Use static_assert for invariants
680
+
681
+ ```cpp
682
+ template <typename T>
683
+ struct Packet {
684
+ static_assert(std::is_trivially_copyable_v<T>,
685
+ "Packet payload must be trivially copyable");
686
+ T payload;
687
+ };
688
+ ```
689
+
690
+ ### Avoid template bloat
691
+
692
+ ```cpp
693
+ // ❌ Bad: full template instantiation for each T, even if only one method varies
694
+ template <typename T>
695
+ class Service {
696
+ void connect() { /* 100 lines of identical code */ }
697
+ void process(T item) { /* type-specific */ }
698
+ };
699
+
700
+ // ✅ Good: factor out type-independent code into a non-template base
701
+ class ServiceBase {
702
+ protected:
703
+ void connect() { /* 100 lines of shared code */ }
704
+ };
705
+
706
+ template <typename T>
707
+ class Service : public ServiceBase {
708
+ void process(T item) { /* type-specific */ }
709
+ };
710
+ ```
711
+
712
+ ---
713
+
714
+ ## Testing
715
+
716
+ ### Framework selection
717
+
718
+ | Framework | Best For |
719
+ |-----------|----------|
720
+ | **Google Test (GTest)** | Large projects, CI, GMock integration |
721
+ | **Catch2** | Header-only, BDD-style, modern C++ |
722
+ | **doctest** | Lightweight, single-header, fast compile |
723
+
724
+ ### Google Test basics
725
+
726
+ ```cpp
727
+ #include <gtest/gtest.h>
728
+ #include "parser.h"
729
+
730
+ TEST(ParserTest, EmptyInputReturnsNull) {
731
+ auto token = parse("");
732
+ EXPECT_EQ(token, nullptr);
733
+ }
734
+
735
+ TEST(ParserTest, ValidInteger) {
736
+ auto token = parse("42");
737
+ ASSERT_NE(token, nullptr);
738
+ EXPECT_EQ(token->type, TokenType::Int);
739
+ EXPECT_EQ(token->value, 42);
740
+ }
741
+
742
+ TEST(ParserTest, NegativeNumber) {
743
+ auto token = parse("-7");
744
+ ASSERT_NE(token, nullptr);
745
+ EXPECT_EQ(token->value, -7);
746
+ }
747
+ ```
748
+
749
+ ### Test fixtures
750
+
751
+ ```cpp
752
+ class DatabaseTest : public ::testing::Test {
753
+ protected:
754
+ void SetUp() override {
755
+ db_ = std::make_unique<Database>(":memory:");
756
+ db_->execute("CREATE TABLE users (id INTEGER, name TEXT)");
757
+ }
758
+
759
+ void TearDown() override {
760
+ db_.reset();
761
+ }
762
+
763
+ std::unique_ptr<Database> db_;
764
+ };
765
+
766
+ TEST_F(DatabaseTest, InsertAndQuery) {
767
+ db_->execute("INSERT INTO users VALUES (1, 'Alice')");
768
+ auto rows = db_->query("SELECT * FROM users");
769
+ ASSERT_EQ(rows.size(), 1);
770
+ EXPECT_EQ(rows[0].get<std::string>("name"), "Alice");
771
+ }
772
+
773
+ TEST_F(DatabaseTest, EmptyTableReturnsNoRows) {
774
+ auto rows = db_->query("SELECT * FROM users");
775
+ EXPECT_TRUE(rows.empty());
776
+ }
777
+ ```
778
+
779
+ ### Mock objects with GMock
780
+
781
+ ```cpp
782
+ #include <gmock/gmock.h>
783
+
784
+ class HttpClient {
785
+ public:
786
+ virtual ~HttpClient() = default;
787
+ virtual HttpResponse get(const std::string& url) = 0;
788
+ };
789
+
790
+ class MockHttpClient : public HttpClient {
791
+ public:
792
+ MOCK_METHOD(HttpResponse, get, (const std::string& url), (override));
793
+ };
794
+
795
+ TEST(UserServiceTest, FetchesUserProfile) {
796
+ MockHttpClient client;
797
+ EXPECT_CALL(client, get("https://api.example.com/user/1"))
798
+ .WillOnce(Return(HttpResponse{200, R"({"name":"Alice"})"}));
799
+
800
+ UserService svc(&client);
801
+ auto profile = svc.get_profile(1);
802
+ EXPECT_EQ(profile.name, "Alice");
803
+ }
804
+ ```
805
+
806
+ ### Test exception safety
807
+
808
+ ```cpp
809
+ TEST(AllocatorTest, ThrowsOnOverflow) {
810
+ EXPECT_THROW(allocate(SIZE_MAX), std::bad_alloc);
811
+ }
812
+
813
+ TEST(AllocatorTest, NoLeakOnException) {
814
+ // Run under ASan to verify no leaks when exception is thrown
815
+ try {
816
+ auto buf = allocate(1024);
817
+ throw std::runtime_error("simulated failure");
818
+ } catch (...) {
819
+ // ASan will catch any leaks
820
+ }
821
+ }
822
+ ```
823
+
824
+ ---
825
+
826
+ ## Tooling and Build Checks
827
+
828
+ ```bash
829
+ # Warnings
830
+ clang++ -Wall -Wextra -Werror -Wconversion -Wshadow -std=c++20 ...
831
+
832
+ # Sanitizers (debug builds)
833
+ clang++ -fsanitize=address,undefined -fno-omit-frame-pointer -g ...
834
+ clang++ -fsanitize=thread -fno-omit-frame-pointer -g ...
835
+
836
+ # Static analysis
837
+ clang-tidy src/*.cpp -- -std=c++20
838
+
839
+ # Formatting
840
+ clang-format -i src/*.cpp include/*.h
841
+ ```
842
+
843
+ ### Recommended compiler flags for safety
844
+
845
+ ```bash
846
+ # Strict mode for new code
847
+ clang++ -std=c++20 -Wall -Wextra -Werror -Wshadow -Wconversion \
848
+ -Wsign-conversion -Wold-style-cast -Wnon-virtual-dtor \
849
+ -Woverloaded-virtual -Wnull-dereference -Wformat=2 \
850
+ -fsanitize=address,undefined -fno-omit-frame-pointer
851
+ ```
852
+
853
+ ---
854
+
855
+ ## Review Checklist
856
+
857
+ ### Safety and Lifetime
858
+ - [ ] Ownership is explicit (RAII, unique_ptr by default)
859
+ - [ ] No dangling references or views
860
+ - [ ] Rule of 0/3/5 followed for resource-owning types
861
+ - [ ] No raw new/delete in business logic
862
+ - [ ] Destructors are noexcept and do not throw
863
+ - [ ] Smart pointer types match ownership semantics (unique vs shared vs weak)
864
+ - [ ] No shared_ptr cycles (use weak_ptr for back-references)
865
+
866
+ ### API and Design
867
+ - [ ] const-correctness is applied consistently
868
+ - [ ] Constructors are explicit where needed
869
+ - [ ] Override/final used for virtual functions
870
+ - [ ] No object slicing (pass by ref or pointer)
871
+ - [ ] Concepts constrain template parameters (C++20)
872
+
873
+ ### Modern Features
874
+ - [ ] constexpr used for compile-time computation where beneficial
875
+ - [ ] Ranges preferred over manual loops for data pipelines (C++20)
876
+ - [ ] std::jthread preferred over std::thread for new code (C++20)
877
+ - [ ] std::expected used for error handling (C++23) where available
878
+
879
+ ### Concurrency
880
+ - [ ] Shared data is protected (mutex or atomics)
881
+ - [ ] Locking order is consistent
882
+ - [ ] No blocking while holding locks
883
+
884
+ ### Performance
885
+ - [ ] Unnecessary allocations avoided (reserve, move, span)
886
+ - [ ] Copies avoided in hot paths
887
+ - [ ] Algorithmic complexity is reasonable
888
+
889
+ ### Testing and Tooling
890
+ - [ ] Unit tests cover happy path, error paths, and edge cases
891
+ - [ ] Builds clean with warnings enabled
892
+ - [ ] Sanitizers run on critical code paths
893
+ - [ ] Static analysis (clang-tidy) results are addressed