C++#
C++ builds on C. tree-sitter-cpp is a superset of tree-sitter-c, so the C node types carry over and the five C-family rules (SAFE106 / SAFE310-313) apply to C++. On top of that, C++ gains its try / catch / throw error-handling rules (SAFE201 / SAFE202 / SAFE203) and two new C++-only idiom rules (SAFE315 / SAFE316). New in v2.8.0.
File extensions#
.cpp, .cxx, .cc, .hpp, .hxx, .hh.
.h ownership: a plain .h header is linted as C, not C++ - content sniffing is out of scope, so a header shared between C and C++ is treated as C. Use the C++-specific header extensions (.hpp, .hxx, .hh) for C++ header linting. This is a documented limitation shared with the C page.
Quick start#
pip install 'safelint[cpp]' # adds .cpp, .cxx, .cc, .hpp, .hxx, .hh
# or
uv add 'safelint[cpp]'
safelint check src/ # lint a directory
The tree-sitter-cpp grammar ships as the opt-in [cpp] extra; the base install bundles no grammars.
Suppression directives#
C++ uses line-comment directives (// form): // nosafe (all rules on the line), // nosafe: SAFE315 (a specific rule), and the file-scope // safelint: ignore. Block-comment (/* */) directives parse but are not recognised, matching every other language's line-directive-only convention.
Rules that fire on C++#
26 rules apply: the cross-language ports, the five C-family rules widened to C and C++, the three try/catch rules (SAFE201 / SAFE202 / SAFE203), and the two C++-only idiom rules (SAFE315 / SAFE316).
C++-only rules#
- SAFE315
raw_new_delete- everynew/deleteexpression. Preferstd::make_unique/std::make_sharedand RAII.std::make_uniquecontains nonewexpression and never fires; a rawnewinside astd::unique_ptr<T>(new T)argument still fires. Overlaps the widened SAFE310 by design (SAFE310 is the Holzmann no-allocation posture, SAFE315 the modern-ownership posture). Disabled by default. - SAFE316
dangerous_casts-reinterpret_castandconst_cast, which defeat the type / const system.static_castanddynamic_castare compiler-checked and stay clean. The flagged list is configurable viadangerous_casts_cpp. Disabled by default.
C-family rules widened to C++#
SAFE106 (nonlocal_jumps), SAFE310 (dynamic_allocation), SAFE311 (complex_macro), SAFE312 (conditional_compilation), and SAFE313 (restricted_pointers) all apply to C++. Two behaviours differ from plain C:
- SAFE310 additionally flags C++
new/deleteexpressions as dynamic allocation (alongside the configuredmalloc-family calls). - SAFE313 naturally exempts smart pointers:
std::unique_ptr<T>is a class template, not apointer_declarator, so it never trips the raw multi-level-pointer check.
Each reads a _cpp-suffixed config list (nonlocal_jump_calls_cpp, allocation_calls_cpp, ...) so a project can diverge its C and C++ knobs.
Error handling (try / catch / throw)#
C++ is the first non-Python home for SAFE201 bare_except: it flags the catch (...) catch-all, which swallows every exception with no binding to inspect or re-raise - the same hazard as Python's bare except:. A typed catch (const E& e) is clean. SAFE202 fires on empty / comment-only / literal-only catch bodies; SAFE203 requires a swallowing catch to log. A std::cerr << ... stream insertion counts as logging (it is a << operator, not a call, so it is recognised specially), as does an spdlog::error(...)-style call; a bare throw; or throw e; counts as a re-raise.
C++ shapes worth knowing#
function_definitioncovers both free functions AND methods;lambda_expressionis a separate function node. A method name is afield_identifier(in-class) or aqualified_identifier(S::m, out-of-line); a free function's name nests underdeclarator.declaratoras in C.- SAFE105 detects a
this->m()self-call in addition to a bare recursive call. - SAFE302 descends into
namespace_definitionbodies, so a namespace-scoped mutable global fires, not just a translation-unit-scope one. - The named casts (
reinterpret_cast<T>(x)) are not dedicated cast nodes: they parse as acall_expressionwhosefunctionis atemplate_function. SAFE316 detects them by that template callee name.
Deliberately not registered for C++#
| Rule | Rationale |
|---|---|
SAFE301 global_state |
No global keyword; namespace / file-scope state is the SAFE302 port. |
SAFE305 wide_scope_declaration |
No var hoisting distinction. |
SAFE401 resource_lifecycle |
RAII makes cleanup language-enforced (same rationale as Rust); raw new / delete discipline is SAFE310 / SAFE315. |
SAFE803 null_dereference |
Nullable-return tracking without types would be noise (same as C). |