Skip to content

🚫 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

// NOLINTNEXTLINE(check-name)
problematic_code();

Multiple Checks

// NOLINTNEXTLINE(check-name1, check-name2, check-name3)
problematic_code();

Entire Function

// NOLINT
void entire_function() {
    // All checks suppressed (use sparingly!)
}

Inline Suppression

int x = getValue();  // NOLINT(check-name) - Reason here

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:

✅ Good: All suppressions have comments
❌ Bad: Suppressions without explanations (why?)

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:

// NOLINTNEXTLINE(specific-check-name) - Reason: [explanation]
code_that_triggers_warning();


🎯 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;
// 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:

  1. Verify reason - Is explanation clear and valid?
  2. Challenge necessity - Could code be fixed instead?
  3. Check scope - Is suppression as narrow as possible?
  4. Ensure documentation - Is there a comment?
  5. 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