🚫 Suppression Guide¶
✅ Valid Suppressions¶
Suppressions are acceptable when: 1. False positive from linter 2. Third-party code you can't modify 3. Performance critical code requiring specific pattern 4. Platform-specific workaround 5. Intentional design decision (with justification)
ALWAYS include explanatory comment!
1. False Positive¶
Example: Magic Numbers That Aren't Magic
// GOOD: Mathematical constants are self-explanatory
// NOLINTNEXTLINE(readability-magic-numbers) - Mathematical constant
constexpr float PI = 3.14159f;
// NOLINTNEXTLINE(readability-magic-numbers) - Speed of sound (m/s)
constexpr float SPEED_OF_SOUND = 343.0f;
// NOLINTNEXTLINE(readability-magic-numbers) - Sample rate (Hz)
constexpr int SAMPLE_RATE = 48000;
Example: SIMD Intrinsics False Positives
#ifdef __SSE__
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - SIMD requires cast
__m128 v = _mm_load_ps(reinterpret_cast<const float*>(buffer));
#endif
Example: Intentional Pointer Arithmetic
// NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - SIMD requires raw pointers
void process_simd(float* data, size_t size) {
for (size_t i = 0; i < size; i += 4) {
__m128 v = _mm_load_ps(data + i); // Pointer arithmetic
// ...
}
}
2. Third-Party Code¶
Example: External Library Headers
// Third-party library we cannot modify
// NOLINT
#include <ThirdPartyLibrary.h>
// NOLINTNEXTLINE - External API requires non-const pointer
void processExternal(float* buffer) {
externalLibrary::process(buffer); // API we don't control
}
Example: Legacy C API
// Legacy C API requires manual memory management
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - C API compatibility
FILE* file = fopen("data.txt", "r");
// ...
// NOLINTNEXTLINE(cppcoreguidelines-owning-memory) - C API cleanup
fclose(file);
3. Performance Critical Code¶
Example: Manual Loop Unrolling
// Performance: Manual unroll is 30% faster than loop (benchmarked)
// NOLINTNEXTLINE(readability-duplicate-code)
void processSIMD(float* data) {
process_sample(data[0]);
process_sample(data[1]);
process_sample(data[2]);
process_sample(data[3]);
}
Example: Inline Assembly
// Performance: Inline assembly for critical path
// NOLINTNEXTLINE(hicpp-no-assembler) - Optimized saturating add
inline int32_t saturating_add(int32_t a, int32_t b) {
int32_t result;
asm("adds %0, %1, %2" : "=r"(result) : "r"(a), "r"(b));
return result;
}
4. Platform-Specific Workaround¶
Example: Windows-Specific Code
#ifdef _WIN32
// Windows requires reinterpret_cast for HWND
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - Windows API requirement
HWND hwnd = reinterpret_cast<HWND>(nativeHandle);
#endif
Example: Alignment Requirements
// ARM NEON requires 16-byte alignment
// NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays) - SIMD requires C arrays
alignas(16) float buffer[64];
5. Intentional Design Decision¶
Example: Public Mutable Member for POD
// Intentional: POD struct for low-latency communication
// NOLINTNEXTLINE(misc-non-private-member-variables-in-classes)
struct AudioBuffer {
float* data; // Direct access for performance
size_t size;
uint32_t flags;
};
Example: Singleton Pattern
// Intentional: Singleton for global config (justified by design doc)
// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Config& Config::getInstance() {
static Config instance; // Thread-safe in C++11
return instance;
}
❌ Invalid Suppressions¶
1. Laziness¶
// ❌ BAD: No justification, just lazy
// NOLINTNEXTLINE
void uglyFunction() {
// Complex, unmaintainable code
}
// ✅ GOOD: Fix the issue
void cleanFunction() {
// Refactored, readable code
}
2. "I'll Fix Later" Syndrome¶
// ❌ BAD: TODO suppressions accumulate
// NOLINTNEXTLINE - TODO: fix this later
void hackyWorkaround() { ... }
// ✅ GOOD: Fix now or create ticket
void properSolution() { ... }
// OR: Track in issue tracker, add issue number
// NOLINTNEXTLINE - Issue #123: Refactor after v2.0 release
3. Suppressing Category Instead of Specific Check¶
// ❌ BAD: Too broad
// NOLINT
void someFunction() { ... }
// ✅ GOOD: Specific check
// NOLINTNEXTLINE(readability-function-cognitive-complexity) - Complex domain logic
void someFunction() { ... }
4. No Explanation¶
// ❌ BAD: Why is this suppressed?
// NOLINTNEXTLINE(bugprone-*)
void dangerousCode() { ... }
// ✅ GOOD: Explanation required
// NOLINTNEXTLINE(bugprone-dangling-handle) - False positive: view outlives source
std::string_view getView() { ... }
🛠️ Suppression Syntax¶
Single Line Suppression¶
Multiple Checks¶
Entire Function¶
Inline Suppression¶
Block Suppression (cppcheck)¶
// cppcheck-suppress checkName
problematic_code();
// cppcheck-suppress [checkName1, checkName2]
more_problematic_code();
📊 Suppression Audit¶
Tracking Suppressions¶
Script to find all suppressions:
#!/bin/bash
# count_suppressions.sh
echo "Suppressions by type:"
grep -r "NOLINTNEXTLINE" src/ | \
sed 's/.*NOLINTNEXTLINE(\([^)]*\)).*/\1/' | \
sort | uniq -c | sort -rn
echo ""
echo "Total suppressions: $(grep -r "NOLINTNEXTLINE" src/ | wc -l)"
Output:
Suppressions by type:
42 readability-magic-numbers
15 cppcoreguidelines-pro-type-reinterpret-cast
8 modernize-use-trailing-return-type
3 performance-unnecessary-copy-initialization
Total suppressions: 68
Red Flags 🚩¶
Too many suppressions overall:
✅ Good: < 50 suppressions in 10k LOC
⚠️ Warning: 50-200 suppressions
❌ Bad: > 200 suppressions (linter is being ignored)
Same check suppressed repeatedly:
✅ Good: Diverse suppressions (legitimate cases)
❌ Bad: Same check suppressed 20+ times (configure linter instead)
No explanations:
Periodic Review¶
Quarterly Suppression Review:
# Generate report
./count_suppressions.sh > suppressions_q1_2025.txt
# Review in team meeting
# Questions:
# 1. Are these still valid?
# 2. Can we fix any now?
# 3. Are there patterns? (configure linter?)
📋 Suppression Checklist¶
Before adding a suppression, ask:
- Is this a real issue? (Fix it if yes)
- Is this a false positive? (Document why)
- Can I configure the linter instead? (Better than suppressing)
- Did I add an explanatory comment? (Required!)
- Is the suppression as narrow as possible? (Specific check, not all)
- Will future developers understand? (Context matters)
Template:
🎯 Best Practices¶
1. Specific > General¶
// ❌ BAD: Suppresses all checks
// NOLINT
void function() { ... }
// ✅ GOOD: Specific check only
// NOLINTNEXTLINE(readability-function-cognitive-complexity)
void function() { ... }
2. Narrow Scope¶
// ❌ BAD: Entire function suppressed
// NOLINT
void function() {
safe_code();
safe_code();
problematic_code(); // Only this line needs suppression
}
// ✅ GOOD: Minimal scope
void function() {
safe_code();
safe_code();
// NOLINTNEXTLINE(check-name) - Reason
problematic_code();
}
3. Document Alternatives Considered¶
// NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
// Reason: Copy is intentional - we modify the copy later
// Alternatives considered:
// 1. Pass by reference - No, we need independent copy
// 2. Clone method - Overkill for simple struct
auto copy = original;
4. Link to Issues/Docs¶
// NOLINTNEXTLINE(bugprone-dangling-handle)
// False positive: See issue #456 for analysis
// Design doc: docs/architecture/string_view_safety.md
std::string_view getView() { ... }
🔍 Review Process¶
Code Review Checklist¶
When reviewing PRs with suppressions:
- Verify reason - Is explanation clear and valid?
- Challenge necessity - Could code be fixed instead?
- Check scope - Is suppression as narrow as possible?
- Ensure documentation - Is there a comment?
- Question patterns - Multiple similar suppressions? (config issue?)
Auto-Check in CI¶
# .github/workflows/lint.yml
- name: Check for unexplained suppressions
run: |
# Fail if NOLINT without comment
if grep -r "NOLINT$" src/; then
echo "Error: Suppressions must have explanatory comments"
exit 1
fi
📚 References¶
- clang-tidy suppressions: https://clang.llvm.org/extra/clang-tidy/#suppressing-undesired-diagnostics
- cppcheck suppressions: https://cppcheck.sourceforge.io/manual.pdf (Section 5)
- C++ Core Guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#S-gsl