Skills included: - pr-reviewer: Adapted for Gitea/GitHub via platform-aware scripts (dropped fetch_pr_data.py and add_inline_comment.py, kept generate_review_files.py) - code-review-excellence: Methodology and checklists (React, TS, Python, etc.) - vercel-react-best-practices: 57 rules for React/Next.js performance - tailwind-design-system: Tailwind CSS v4 patterns, CVA, design tokens New shell scripts added to ~/.claude/scripts/git/: - pr-diff.sh: Get PR diff (GitHub gh / Gitea API) - pr-metadata.sh: Get PR metadata as normalized JSON Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
5.5 KiB
5.5 KiB
C Code Review Guide
C code review guide focused on memory safety, undefined behavior, and portability. Examples assume C11.
Table of Contents
- Pointer and Buffer Safety
- Ownership and Resource Management
- Undefined Behavior Pitfalls
- Integer Types and Overflow
- Error Handling
- Concurrency
- Macros and Preprocessor
- API Design and Const
- Tooling and Build Checks
- Review Checklist
Pointer and Buffer Safety
Always carry size with buffers
// ? Bad: ignores destination size
bool copy_name(char *dst, size_t dst_size, const char *src) {
strcpy(dst, src);
return true;
}
// ? Good: validate size and terminate
bool copy_name(char *dst, size_t dst_size, const char *src) {
size_t len = strlen(src);
if (len + 1 > dst_size) {
return false;
}
memcpy(dst, src, len + 1);
return true;
}
Avoid dangerous APIs
Prefer snprintf, fgets, and explicit bounds over gets, strcpy, or sprintf.
// ? Bad: unbounded write
sprintf(buf, "%s", input);
// ? Good: bounded write
snprintf(buf, buf_size, "%s", input);
Use the right copy primitive
// ? Bad: memcpy with overlapping regions
memcpy(dst, src, len);
// ? Good: memmove handles overlap
memmove(dst, src, len);
Ownership and Resource Management
One allocation, one free
Track ownership and clean up on every error path.
// ? Good: cleanup label avoids leaks
int load_file(const char *path) {
int rc = -1;
FILE *f = NULL;
char *buf = NULL;
f = fopen(path, "rb");
if (!f) {
goto cleanup;
}
buf = malloc(4096);
if (!buf) {
goto cleanup;
}
if (fread(buf, 1, 4096, f) == 0) {
goto cleanup;
}
rc = 0;
cleanup:
free(buf);
if (f) {
fclose(f);
}
return rc;
}
Undefined Behavior Pitfalls
Common UB patterns
// ? Bad: use after free
char *p = malloc(10);
free(p);
p[0] = 'a';
// ? Bad: uninitialized read
int x;
if (x > 0) { /* UB */ }
// ? Bad: signed overflow
int sum = a + b;
Avoid pointer arithmetic past the object
// ? Bad: pointer past the end then dereference
int arr[4];
int *p = arr + 4;
int v = *p; // UB
Integer Types and Overflow
Avoid signed/unsigned surprises
// ? Bad: negative converted to large size_t
int len = -1;
size_t n = len;
// ? Good: validate before converting
if (len < 0) {
return -1;
}
size_t n = (size_t)len;
Check for overflow in size calculations
// ? Bad: potential overflow in multiplication
size_t bytes = count * sizeof(Item);
// ? Good: check before multiplying
if (count > SIZE_MAX / sizeof(Item)) {
return NULL;
}
size_t bytes = count * sizeof(Item);
Error Handling
Always check return values
// ? Bad: ignore errors
fread(buf, 1, size, f);
// ? Good: handle errors
size_t read = fread(buf, 1, size, f);
if (read != size && ferror(f)) {
return -1;
}
Consistent error contracts
- Use a clear convention: 0 for success, negative for failure.
- Document ownership rules on success and failure.
- If using
errno, set it only for actual failures.
Concurrency
volatile is not synchronization
// ? Bad: data race
volatile int stop = 0;
void worker(void) {
while (!stop) { /* ... */ }
}
// ? Good: C11 atomics
_Atomic int stop = 0;
void worker(void) {
while (!atomic_load(&stop)) { /* ... */ }
}
Use mutexes for shared state
Protect shared data with pthread_mutex_t or equivalent. Avoid holding locks while doing I/O.
Macros and Preprocessor
Parenthesize arguments
// ? Bad: macro with side effects
#define MIN(a, b) ((a) < (b) ? (a) : (b))
int x = MIN(i++, j++);
// ? Good: static inline function
static inline int min_int(int a, int b) {
return a < b ? a : b;
}
API Design and Const
Const-correctness and sizes
// ? Good: explicit size and const input
int hash_bytes(const uint8_t *data, size_t len, uint8_t *out);
Document nullability
Clearly document whether pointers may be NULL. Prefer returning error codes instead of NULL when possible.
Tooling and Build Checks
# Warnings
clang -Wall -Wextra -Werror -Wconversion -Wshadow -std=c11 ...
# Sanitizers (debug builds)
clang -fsanitize=address,undefined -fno-omit-frame-pointer -g ...
clang -fsanitize=thread -fno-omit-frame-pointer -g ...
# Static analysis
clang-tidy src/*.c -- -std=c11
cppcheck --enable=warning,performance,portability src/
# Formatting
clang-format -i src/*.c include/*.h
Review Checklist
Memory and UB
- All buffers have explicit size parameters
- No out-of-bounds access or pointer arithmetic past objects
- No use after free or uninitialized reads
- Signed overflow and shift rules are respected
API and Design
- Ownership rules are documented and consistent
- const-correctness is applied for inputs
- Error contracts are clear and consistent
Concurrency
- No data races on shared state
- volatile is not used for synchronization
- Locks are held for minimal time
Tooling and Tests
- Builds clean with warnings enabled
- Sanitizers run on critical code paths
- Static analysis results are addressed