@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,890 @@
1
+ # C Code Review Guide
2
+
3
+ > C code review guide focused on memory safety, undefined behavior, portability, testing, and secure coding. Examples assume C11/C17.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Pointer and Buffer Safety](#pointer-and-buffer-safety)
8
+ - [Ownership and Resource Management](#ownership-and-resource-management)
9
+ - [Undefined Behavior Pitfalls](#undefined-behavior-pitfalls)
10
+ - [Integer Types and Overflow](#integer-types-and-overflow)
11
+ - [Error Handling](#error-handling)
12
+ - [Concurrency](#concurrency)
13
+ - [Macros and Preprocessor](#macros-and-preprocessor)
14
+ - [API Design and Const](#api-design-and-const)
15
+ - [Secure Coding Practices](#secure-coding-practices)
16
+ - [Cross-Platform Portability](#cross-platform-portability)
17
+ - [Testing](#testing)
18
+ - [Tooling and Build Checks](#tooling-and-build-checks)
19
+ - [Review Checklist](#review-checklist)
20
+
21
+ ---
22
+
23
+ ## Pointer and Buffer Safety
24
+
25
+ ### Always carry size with buffers
26
+
27
+ ```c
28
+ // ❌ Bad: ignores destination size
29
+ bool copy_name(char *dst, size_t dst_size, const char *src) {
30
+ strcpy(dst, src);
31
+ return true;
32
+ }
33
+
34
+ // ✅ Good: validate size and terminate
35
+ bool copy_name(char *dst, size_t dst_size, const char *src) {
36
+ size_t len = strlen(src);
37
+ if (len + 1 > dst_size) {
38
+ return false;
39
+ }
40
+ memcpy(dst, src, len + 1);
41
+ return true;
42
+ }
43
+ ```
44
+
45
+ ### Avoid dangerous APIs
46
+
47
+ Prefer `snprintf`, `fgets`, and explicit bounds over `gets`, `strcpy`, or `sprintf`.
48
+
49
+ ```c
50
+ // ❌ Bad: unbounded write
51
+ sprintf(buf, "%s", input);
52
+
53
+ // ✅ Good: bounded write
54
+ snprintf(buf, buf_size, "%s", input);
55
+ ```
56
+
57
+ ### Use the right copy primitive
58
+
59
+ ```c
60
+ // ❌ Bad: memcpy with overlapping regions
61
+ memcpy(dst, src, len);
62
+
63
+ // ✅ Good: memmove handles overlap
64
+ memmove(dst, src, len);
65
+ ```
66
+
67
+ ### Validate pointer arguments
68
+
69
+ ```c
70
+ // ❌ Bad: no NULL check
71
+ int process(char *buf, size_t len) {
72
+ buf[0] = '\0';
73
+ return 0;
74
+ }
75
+
76
+ // ✅ Good: validate before use
77
+ int process(char *buf, size_t len) {
78
+ if (!buf || len == 0) {
79
+ return -EINVAL;
80
+ }
81
+ buf[0] = '\0';
82
+ return 0;
83
+ }
84
+ ```
85
+
86
+ ### Beware of pointer-to-pointer pitfalls
87
+
88
+ ```c
89
+ // ❌ Bad: caller cannot distinguish success from failure
90
+ void allocate(int **out) {
91
+ *out = malloc(sizeof(int));
92
+ }
93
+
94
+ // ✅ Good: return status, set output only on success
95
+ int allocate(int **out) {
96
+ if (!out) return -EINVAL;
97
+ int *p = malloc(sizeof(int));
98
+ if (!p) return -ENOMEM;
99
+ *out = p;
100
+ return 0;
101
+ }
102
+ ```
103
+
104
+ ---
105
+
106
+ ## Ownership and Resource Management
107
+
108
+ ### One allocation, one free
109
+
110
+ Track ownership and clean up on every error path.
111
+
112
+ ```c
113
+ // ✅ Good: cleanup label avoids leaks
114
+ int load_file(const char *path) {
115
+ int rc = -1;
116
+ FILE *f = NULL;
117
+ char *buf = NULL;
118
+
119
+ f = fopen(path, "rb");
120
+ if (!f) {
121
+ goto cleanup;
122
+ }
123
+ buf = malloc(4096);
124
+ if (!buf) {
125
+ goto cleanup;
126
+ }
127
+
128
+ if (fread(buf, 1, 4096, f) == 0) {
129
+ goto cleanup;
130
+ }
131
+
132
+ rc = 0;
133
+
134
+ cleanup:
135
+ free(buf);
136
+ if (f) {
137
+ fclose(f);
138
+ }
139
+ return rc;
140
+ }
141
+ ```
142
+
143
+ ### Document ownership transfer
144
+
145
+ ```c
146
+ // ✅ Good: comment clarifies that caller takes ownership
147
+ // Caller must free() the returned buffer.
148
+ char *read_line(FILE *f);
149
+
150
+ // ✅ Good: comment clarifies that callee does NOT take ownership
151
+ // The function borrows `buf`; caller retains ownership.
152
+ int parse_header(const char *buf, size_t len, struct Header *out);
153
+ ```
154
+
155
+ ### Free exactly once, set pointer to NULL
156
+
157
+ ```c
158
+ // ❌ Bad: double free possible
159
+ void destroy(struct Cache *c) {
160
+ free(c->entries);
161
+ // caller might call destroy() again → double free
162
+ }
163
+
164
+ // ✅ Good: NULL after free prevents double free
165
+ void destroy(struct Cache *c) {
166
+ if (!c) return;
167
+ free(c->entries);
168
+ c->entries = NULL;
169
+ c->count = 0;
170
+ }
171
+ ```
172
+
173
+ ---
174
+
175
+ ## Undefined Behavior Pitfalls
176
+
177
+ ### Signed integer overflow
178
+
179
+ Signed overflow is UB in C; unsigned wraps around.
180
+
181
+ ```c
182
+ // ❌ Bad: signed overflow is UB
183
+ int sum = INT_MAX + 1; // undefined behavior
184
+
185
+ // ✅ Good: check before overflow
186
+ if (a > 0 && b > INT_MAX - a) {
187
+ return -EOVERFLOW;
188
+ }
189
+ int sum = a + b;
190
+ ```
191
+
192
+ ### Dangling pointers
193
+
194
+ ```c
195
+ // ❌ Bad: returning pointer to local array
196
+ char *greet(void) {
197
+ char buf[64];
198
+ snprintf(buf, sizeof(buf), "hello");
199
+ return buf; // UB: buf is gone when function returns
200
+ }
201
+
202
+ // ✅ Good: caller provides buffer or use static storage
203
+ void greet(char *out, size_t out_size) {
204
+ snprintf(out, out_size, "hello");
205
+ }
206
+ ```
207
+
208
+ ### Uninitialized variables
209
+
210
+ ```c
211
+ // ❌ Bad: x may be anything
212
+ int x;
213
+ if (x > 0) { /* UB: reading uninitialized automatic variable */ }
214
+
215
+ // ✅ Good: always initialize
216
+ int x = 0;
217
+ if (x > 0) { /* well-defined */ }
218
+ ```
219
+
220
+ ### Sequence point violations
221
+
222
+ ```c
223
+ // ❌ Bad: undefined — order of evaluation of operands
224
+ int i = 0;
225
+ int a[] = { i++, i++ }; // UB: two modifications without sequence point
226
+
227
+ // ❌ Bad: modification and read without sequence point
228
+ int j = i + i++; // UB
229
+
230
+ // ✅ Good: separate statements
231
+ int a0 = i++;
232
+ int a1 = i++;
233
+ int a[] = { a0, a1 };
234
+ ```
235
+
236
+ ### Strict aliasing violations
237
+
238
+ ```c
239
+ // ❌ Bad: violates strict aliasing
240
+ float f = 3.14f;
241
+ int i = *(int *)&f; // UB
242
+
243
+ // ✅ Good: use memcpy or union (C11 allows type-punning via union)
244
+ int i;
245
+ memcpy(&i, &f, sizeof(i));
246
+
247
+ // ✅ Also acceptable in C11:
248
+ union { float f; int i; } u;
249
+ u.f = 3.14f;
250
+ int i = u.i;
251
+ ```
252
+
253
+ ### Shift operations
254
+
255
+ ```c
256
+ // ❌ Bad: shift by negative or >= width is UB
257
+ int x = 1 << 32; // UB if int is 32-bit
258
+ int y = 1 << -1; // UB
259
+
260
+ // ✅ Good: validate shift amount
261
+ if (shift >= 0 && shift < (int)(sizeof(int) * CHAR_BIT)) {
262
+ int result = 1 << shift;
263
+ }
264
+ ```
265
+
266
+ ---
267
+
268
+ ## Integer Types and Overflow
269
+
270
+ ### Avoid signed/unsigned surprises
271
+
272
+ ```c
273
+ // ❌ Bad: negative converted to large size_t
274
+ int len = -1;
275
+ size_t n = len; // wraps to SIZE_MAX
276
+
277
+ // ✅ Good: validate before converting
278
+ if (len < 0) {
279
+ return -1;
280
+ }
281
+ size_t n = (size_t)len;
282
+ ```
283
+
284
+ ### Check for overflow in size calculations
285
+
286
+ ```c
287
+ // ❌ Bad: potential overflow in multiplication
288
+ size_t bytes = count * sizeof(Item);
289
+
290
+ // ✅ Good: check before multiplying
291
+ if (count > SIZE_MAX / sizeof(Item)) {
292
+ return NULL;
293
+ }
294
+ size_t bytes = count * sizeof(Item);
295
+ ```
296
+
297
+ ### Use fixed-width types for binary protocols
298
+
299
+ ```c
300
+ // ❌ Bad: int size varies by platform
301
+ struct PacketHeader {
302
+ int type;
303
+ int length;
304
+ };
305
+
306
+ // ✅ Good: explicit widths for wire format
307
+ #include <stdint.h>
308
+ struct PacketHeader {
309
+ uint32_t type;
310
+ uint32_t length;
311
+ };
312
+ ```
313
+
314
+ ### Beware of implicit promotion
315
+
316
+ ```c
317
+ // ❌ Bad: uint8_t promotes to int in arithmetic
318
+ uint8_t a = 200, b = 100;
319
+ uint8_t sum = a + b; // truncation: 300 → 44
320
+
321
+ // ✅ Good: be explicit about width
322
+ uint16_t sum = (uint16_t)a + (uint16_t)b; // 300
323
+ ```
324
+
325
+ ---
326
+
327
+ ## Error Handling
328
+
329
+ ### Always check return values
330
+
331
+ ```c
332
+ // ❌ Bad: ignore errors
333
+ fread(buf, 1, size, f);
334
+
335
+ // ✅ Good: handle errors
336
+ size_t read = fread(buf, 1, size, f);
337
+ if (read != size && ferror(f)) {
338
+ return -1;
339
+ }
340
+ ```
341
+
342
+ ### Consistent error contracts
343
+
344
+ - Use a clear convention: 0 for success, negative for failure.
345
+ - Document ownership rules on success and failure.
346
+ - If using `errno`, set it only for actual failures.
347
+
348
+ ```c
349
+ // ✅ Good: clear error contract with errno
350
+ // Returns 0 on success, -1 on failure (sets errno).
351
+ // On failure, *out is unchanged.
352
+ int parse_int(const char *s, int *out);
353
+ ```
354
+
355
+ ### Avoid errno across function boundaries
356
+
357
+ ```c
358
+ // ❌ Bad: errno may be overwritten by intermediate calls
359
+ errno = 0;
360
+ long val = strtol(s, &end, 10);
361
+ log_debug("parsed: %ld", val); // might change errno!
362
+ if (errno != 0) { /* unreliable */ }
363
+
364
+ // ✅ Good: capture errno immediately
365
+ errno = 0;
366
+ long val = strtol(s, &end, 10);
367
+ int saved_errno = errno;
368
+ log_debug("parsed: %ld", val);
369
+ if (saved_errno != 0) { /* reliable */ }
370
+ ```
371
+
372
+ ---
373
+
374
+ ## Concurrency
375
+
376
+ ### volatile is not synchronization
377
+
378
+ ```c
379
+ // ❌ Bad: data race
380
+ volatile int stop = 0;
381
+ void worker(void) {
382
+ while (!stop) { /* ... */ }
383
+ }
384
+
385
+ // ✅ Good: C11 atomics
386
+ _Atomic int stop = 0;
387
+ void worker(void) {
388
+ while (!atomic_load(&stop)) { /* ... */ }
389
+ }
390
+ ```
391
+
392
+ ### Use mutexes for shared state
393
+
394
+ Protect shared data with `pthread_mutex_t` or equivalent. Avoid holding locks while doing I/O.
395
+
396
+ ```c
397
+ // ✅ Good: mutex + RAII-style cleanup
398
+ static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;
399
+ static int g_counter = 0;
400
+
401
+ void increment(void) {
402
+ pthread_mutex_lock(&g_lock);
403
+ g_counter++;
404
+ pthread_mutex_unlock(&g_lock);
405
+ }
406
+ ```
407
+
408
+ ### Avoid lock ordering issues
409
+
410
+ ```c
411
+ // ❌ Bad: inconsistent lock ordering → deadlock
412
+ // Thread 1: lock(A); lock(B);
413
+ // Thread 2: lock(B); lock(A);
414
+
415
+ // ✅ Good: always acquire locks in the same order
416
+ // All threads: lock(A); lock(B);
417
+ ```
418
+
419
+ ---
420
+
421
+ ## Macros and Preprocessor
422
+
423
+ ### Parenthesize arguments
424
+
425
+ ```c
426
+ // ❌ Bad: macro with side effects
427
+ #define MIN(a, b) ((a) < (b) ? (a) : (b))
428
+ int x = MIN(i++, j++); // evaluates argument twice
429
+
430
+ // ✅ Good: static inline function
431
+ static inline int min_int(int a, int b) {
432
+ return a < b ? a : b;
433
+ }
434
+ ```
435
+
436
+ ### Multi-statement macros
437
+
438
+ ```c
439
+ // ❌ Bad: breaks in if-else without braces
440
+ #define LOG_AND_RETURN(msg) \
441
+ fprintf(stderr, "%s\n", msg); \
442
+ return -1
443
+
444
+ // ✅ Good: do { ... } while(0) idiom
445
+ #define LOG_AND_RETURN(msg) do { \
446
+ fprintf(stderr, "%s\n", msg); \
447
+ return -1; \
448
+ } while (0)
449
+ ```
450
+
451
+ ### Include guards
452
+
453
+ ```c
454
+ // ✅ Good: traditional include guard
455
+ #ifndef MY_HEADER_H
456
+ #define MY_HEADER_H
457
+ // ... declarations ...
458
+ #endif /* MY_HEADER_H */
459
+
460
+ // ✅ Also acceptable (non-standard but widely supported):
461
+ #pragma once
462
+ ```
463
+
464
+ ---
465
+
466
+ ## API Design and Const
467
+
468
+ ### Const-correctness and sizes
469
+
470
+ ```c
471
+ // ✅ Good: explicit size and const input
472
+ int hash_bytes(const uint8_t *data, size_t len, uint8_t *out);
473
+ ```
474
+
475
+ ### Document nullability
476
+
477
+ Clearly document whether pointers may be NULL. Prefer returning error codes instead of NULL when possible.
478
+
479
+ ```c
480
+ // ✅ Good: document contract in the header
481
+ // @param name Non-NULL, NUL-terminated string.
482
+ // @param out Non-NULL output pointer.
483
+ // @return 0 on success, -EINVAL if name or out is NULL.
484
+ int lookup(const char *name, struct Result *out);
485
+ ```
486
+
487
+ ### Opaque types for encapsulation
488
+
489
+ ```c
490
+ // ✅ Good: header exposes only a pointer
491
+ typedef struct Parser Parser;
492
+
493
+ Parser *parser_create(const char *input);
494
+ int parser_next(Parser *p, struct Token *out);
495
+ void parser_destroy(Parser *p);
496
+ ```
497
+
498
+ ---
499
+
500
+ ## Secure Coding Practices
501
+
502
+ ### CERT C: buffer overflow prevention
503
+
504
+ ```c
505
+ // ❌ Bad: strncpy does NOT guarantee NUL termination
506
+ char dst[32];
507
+ strncpy(dst, src, sizeof(dst)); // if src >= 32 bytes, dst is not terminated!
508
+
509
+ // ✅ Good: explicit NUL termination after strncpy
510
+ char dst[32];
511
+ strncpy(dst, src, sizeof(dst) - 1);
512
+ dst[sizeof(dst) - 1] = '\0';
513
+
514
+ // ✅ Better: use snprintf for bounded string copy
515
+ char dst[32];
516
+ snprintf(dst, sizeof(dst), "%s", src);
517
+ ```
518
+
519
+ ### Format string vulnerability
520
+
521
+ ```c
522
+ // ❌ Bad: user-controlled format string
523
+ printf(user_input); // if user_input = "%x %x %x", reads stack
524
+
525
+ // ✅ Good: always use a format literal
526
+ printf("%s", user_input);
527
+ ```
528
+
529
+ ### Integer overflow in allocation
530
+
531
+ ```c
532
+ // ❌ Bad: count * size may overflow before malloc sees it
533
+ void *items = malloc(count * sizeof(Item));
534
+
535
+ // ✅ Good: check for overflow
536
+ if (count != 0 && SIZE_MAX / count < sizeof(Item)) {
537
+ errno = ENOMEM;
538
+ return NULL;
539
+ }
540
+ void *items = malloc(count * sizeof(Item));
541
+
542
+ // ✅ Also good: use calloc (checks internally)
543
+ Item *items = calloc(count, sizeof(Item));
544
+ ```
545
+
546
+ ### Validate external input lengths
547
+
548
+ ```c
549
+ // ❌ Bad: trusting header-declared length
550
+ struct Msg { uint32_t len; char data[]; };
551
+ void handle(struct Msg *m) {
552
+ char buf[256];
553
+ memcpy(buf, m->data, m->len); // attacker controls m->len
554
+ }
555
+
556
+ // ✅ Good: validate before use
557
+ void handle(struct Msg *m, size_t total_size) {
558
+ if (m->len > total_size - sizeof(struct Msg)) {
559
+ return -EINVAL;
560
+ }
561
+ char buf[256];
562
+ if (m->len > sizeof(buf)) {
563
+ return -E2BIG;
564
+ }
565
+ memcpy(buf, m->data, m->len);
566
+ }
567
+ ```
568
+
569
+ ### Avoid TOCTOU race conditions
570
+
571
+ ```c
572
+ // ❌ Bad: check-then-use is a race (TOCTOU)
573
+ if (access(path, R_OK) == 0) {
574
+ FILE *f = fopen(path, "r"); // file may have changed between access() and fopen()
575
+ }
576
+
577
+ // ✅ Good: try and check the result
578
+ FILE *f = fopen(path, "r");
579
+ if (!f) {
580
+ // handle error (ENOENT, EACCES, etc.)
581
+ }
582
+ ```
583
+
584
+ ### Secure temporary files
585
+
586
+ ```c
587
+ // ❌ Bad: predictable name
588
+ char path[] = "/tmp/myapp_XXXXXX";
589
+ FILE *f = fopen(path, "w"); // predictable, race condition
590
+
591
+ // ✅ Good: mkstemp creates and opens atomically
592
+ char tmpl[] = "/tmp/myapp_XXXXXX";
593
+ int fd = mkstemp(tmpl);
594
+ if (fd < 0) { /* handle error */ }
595
+ FILE *f = fdopen(fd, "w");
596
+ ```
597
+
598
+ ---
599
+
600
+ ## Cross-Platform Portability
601
+
602
+ ### Preprocessor conditionals best practices
603
+
604
+ ```c
605
+ // ❌ Bad: nested #ifdef soup
606
+ #ifdef _WIN32
607
+ #ifdef _WIN64
608
+ // 64-bit Windows
609
+ #else
610
+ // 32-bit Windows
611
+ #endif
612
+ #else
613
+ #ifdef __linux__
614
+ // Linux
615
+ #endif
616
+ #endif
617
+
618
+ // ✅ Good: abstract behind feature macros
619
+ #if defined(PLATFORM_WINDOWS)
620
+ #include "platform_win.h"
621
+ #elif defined(PLATFORM_LINUX)
622
+ #include "platform_linux.h"
623
+ #elif defined(PLATFORM_MACOS)
624
+ #include "platform_macos.h"
625
+ #else
626
+ #error "Unsupported platform"
627
+ #endif
628
+ ```
629
+
630
+ ### Byte order (endianness)
631
+
632
+ ```c
633
+ // ❌ Bad: assumes little-endian
634
+ uint32_t read_u32(const uint8_t *buf) {
635
+ return *(const uint32_t *)buf; // alignment + endianness issues
636
+ }
637
+
638
+ // ✅ Good: explicit byte-order handling
639
+ static inline uint32_t read_u32_le(const uint8_t *buf) {
640
+ return (uint32_t)buf[0]
641
+ | ((uint32_t)buf[1] << 8)
642
+ | ((uint32_t)buf[2] << 16)
643
+ | ((uint32_t)buf[3] << 24);
644
+ }
645
+
646
+ static inline uint32_t read_u32_be(const uint8_t *buf) {
647
+ return ((uint32_t)buf[0] << 24)
648
+ | ((uint32_t)buf[1] << 16)
649
+ | ((uint32_t)buf[2] << 8)
650
+ | (uint32_t)buf[3];
651
+ }
652
+ ```
653
+
654
+ ### Alignment-aware access
655
+
656
+ ```c
657
+ // ❌ Bad: unaligned access is UB on many architectures
658
+ uint32_t val = *(const uint32_t *)ptr;
659
+
660
+ // ✅ Good: memcpy is safe for any alignment
661
+ uint32_t val;
662
+ memcpy(&val, ptr, sizeof(val));
663
+ ```
664
+
665
+ ### Avoid platform-specific extensions in portable code
666
+
667
+ ```c
668
+ // ❌ Bad: GCC extension in shared code
669
+ typeof(x) y = x;
670
+
671
+ // ✅ Good: use standard C or isolate extensions
672
+ // In a platform-specific header:
673
+ #ifdef __GNUC__
674
+ #define TYPEOF(x) typeof(x)
675
+ #else
676
+ #define TYPEOF(x) decltype(x) /* C++23 or compiler-specific */
677
+ #endif
678
+ ```
679
+
680
+ ### Use feature detection, not platform detection
681
+
682
+ ```c
683
+ // ❌ Bad: assumes POSIX because Linux
684
+ #ifdef __linux__
685
+ #include <sys/mman.h>
686
+ #endif
687
+
688
+ // ✅ Good: feature test via CMake/configure
689
+ #ifdef HAVE_MMAP
690
+ #include <sys/mman.h>
691
+ #endif
692
+ ```
693
+
694
+ ---
695
+
696
+ ## Testing
697
+
698
+ ### Choosing a test framework
699
+
700
+ | Framework | Use Case | Notes |
701
+ |-----------|----------|-------|
702
+ | **Unity** | Embedded / bare-metal | Single-file, no dependencies, C89 compatible |
703
+ | **CUnit** | Desktop / CI | Richer assertions, HTML/XML output |
704
+ | **CMocka** | System-level code | Mocking via function pointers, works with `setjmp`/`longjmp` |
705
+
706
+ ### Basic test structure with Unity
707
+
708
+ ```c
709
+ #include "unity.h"
710
+ #include "parser.h"
711
+
712
+ void setUp(void) { /* runs before each test */ }
713
+ void tearDown(void) { /* runs after each test */ }
714
+
715
+ void test_parse_empty_string_returns_null(void) {
716
+ struct Token *t = parse("");
717
+ TEST_ASSERT_NULL(t);
718
+ }
719
+
720
+ void test_parse_valid_integer(void) {
721
+ struct Token *t = parse("42");
722
+ TEST_ASSERT_NOT_NULL(t);
723
+ TEST_ASSERT_EQUAL_INT(TOKEN_INT, t->type);
724
+ TEST_ASSERT_EQUAL_INT(42, t->value);
725
+ token_free(t);
726
+ }
727
+
728
+ void test_parse_negative_number(void) {
729
+ struct Token *t = parse("-7");
730
+ TEST_ASSERT_NOT_NULL(t);
731
+ TEST_ASSERT_EQUAL_INT(-7, t->value);
732
+ token_free(t);
733
+ }
734
+
735
+ int main(void) {
736
+ UNITY_BEGIN();
737
+ RUN_TEST(test_parse_empty_string_returns_null);
738
+ RUN_TEST(test_parse_valid_integer);
739
+ RUN_TEST(test_parse_negative_number);
740
+ return UNITY_END();
741
+ }
742
+ ```
743
+
744
+ ### Test isolation: mock system calls
745
+
746
+ ```c
747
+ // ✅ Good: inject dependencies for testability
748
+ // Production code:
749
+ struct FileOps {
750
+ int (*read)(void *buf, size_t size, void *ctx);
751
+ void *ctx;
752
+ };
753
+
754
+ int load_config(const struct FileOps *ops, struct Config *out);
755
+
756
+ // Test code:
757
+ static int mock_read(void *buf, size_t size, void *ctx) {
758
+ const char *data = (const char *)ctx;
759
+ size_t len = strlen(data);
760
+ if (len < size) size = len;
761
+ memcpy(buf, data, size);
762
+ return (int)size;
763
+ }
764
+
765
+ void test_load_config_with_mock(void) {
766
+ const char *fake_data = "key=value\n";
767
+ struct FileOps ops = { .read = mock_read, .ctx = (void *)fake_data };
768
+ struct Config cfg;
769
+ int rc = load_config(&ops, &cfg);
770
+ TEST_ASSERT_EQUAL_INT(0, rc);
771
+ TEST_ASSERT_EQUAL_STRING("value", cfg.key);
772
+ }
773
+ ```
774
+
775
+ ### Memory leak testing with sanitizers
776
+
777
+ ```bash
778
+ # Run tests under AddressSanitizer
779
+ cc -fsanitize=address -fno-omit-frame-pointer -g -o test_runner tests/*.c src/*.c
780
+ ./test_runner
781
+
782
+ # Run tests under Valgrind
783
+ cc -g -O0 -o test_runner tests/*.c src/*.c
784
+ valgrind --leak-check=full --error-exitcode=1 ./test_runner
785
+ ```
786
+
787
+ ```c
788
+ // ✅ Good: test that error paths don't leak
789
+ void test_parse_invalid_frees_resources(void) {
790
+ // Valgrind/ASan will catch any leaks from this call
791
+ struct Token *t = parse("not_a_number");
792
+ TEST_ASSERT_NULL(t);
793
+ // If parse() allocated internal state and forgot to free on error,
794
+ // the sanitizer will report it.
795
+ }
796
+ ```
797
+
798
+ ### Test edge cases systematically
799
+
800
+ ```c
801
+ void test_edge_cases(void) {
802
+ // Zero-length input
803
+ TEST_ASSERT_EQUAL_INT(-EINVAL, process(NULL, 0));
804
+
805
+ // Maximum valid input
806
+ char buf[256];
807
+ memset(buf, 'a', sizeof(buf) - 1);
808
+ buf[sizeof(buf) - 1] = '\0';
809
+ TEST_ASSERT_EQUAL_INT(0, process(buf, sizeof(buf) - 1));
810
+
811
+ // One byte over the limit
812
+ TEST_ASSERT_EQUAL_INT(-E2BIG, process(buf, sizeof(buf)));
813
+ }
814
+ ```
815
+
816
+ ---
817
+
818
+ ## Tooling and Build Checks
819
+
820
+ ```bash
821
+ # Warnings
822
+ clang -Wall -Wextra -Werror -Wconversion -Wshadow -std=c11 ...
823
+
824
+ # Sanitizers (debug builds)
825
+ clang -fsanitize=address,undefined -fno-omit-frame-pointer -g ...
826
+ clang -fsanitize=thread -fno-omit-frame-pointer -g ...
827
+
828
+ # Static analysis
829
+ clang-tidy src/*.c -- -std=c11
830
+ cppcheck --enable=warning,performance,portability src/
831
+
832
+ # Formatting
833
+ clang-format -i src/*.c include/*.h
834
+ ```
835
+
836
+ ### CI integration checklist
837
+
838
+ ```bash
839
+ # Typical CI pipeline for a C project
840
+ clang -Wall -Wextra -Werror -std=c11 -c src/*.c # compile with strict warnings
841
+ clang -fsanitize=address,undefined -g -o test test/*.c src/*.c # sanitizer build
842
+ ./test # run tests
843
+ valgrind --leak-check=full --error-exitcode=1 ./test # memory check
844
+ cppcheck --error-exitcode=1 --enable=all src/ # static analysis
845
+ ```
846
+
847
+ ---
848
+
849
+ ## Review Checklist
850
+
851
+ ### Memory and UB
852
+ - [ ] All buffers have explicit size parameters
853
+ - [ ] No out-of-bounds access or pointer arithmetic past objects
854
+ - [ ] No use after free or uninitialized reads
855
+ - [ ] Signed overflow and shift rules are respected
856
+ - [ ] Strict aliasing rules are respected
857
+ - [ ] Sequence point rules are respected
858
+
859
+ ### Secure Coding
860
+ - [ ] No format string vulnerabilities (user input never used as format)
861
+ - [ ] No unchecked allocation sizes (overflow in count * size)
862
+ - [ ] No TOCTOU races on file operations
863
+ - [ ] External input lengths are validated before use
864
+ - [ ] Temporary files use mkstemp or equivalent
865
+
866
+ ### API and Design
867
+ - [ ] Ownership rules are documented and consistent
868
+ - [ ] const-correctness is applied for inputs
869
+ - [ ] Error contracts are clear and consistent
870
+ - [ ] Pointer nullability is documented
871
+ - [ ] Opaque types used for encapsulation
872
+
873
+ ### Portability
874
+ - [ ] No unaligned memory access
875
+ - [ ] Byte order handled explicitly for wire/binary formats
876
+ - [ ] Fixed-width types used for binary protocols
877
+ - [ ] Platform-specific code isolated behind feature macros
878
+
879
+ ### Concurrency
880
+ - [ ] No data races on shared state
881
+ - [ ] volatile is not used for synchronization
882
+ - [ ] Locks are held for minimal time
883
+ - [ ] Lock ordering is consistent
884
+
885
+ ### Testing and Tooling
886
+ - [ ] Unit tests cover happy path, error paths, and edge cases
887
+ - [ ] Builds clean with warnings enabled (-Wall -Wextra -Werror)
888
+ - [ ] Sanitizers (ASan, UBSan) run on critical code paths
889
+ - [ ] Valgrind or ASan confirms no memory leaks
890
+ - [ ] Static analysis results are addressed