What this server understands about C++: how a file is identified as C++, which language features it reads, what the
semantic layer can answer, what each LSP feature gives, and which diagnostics exist. Configuration and the limits are in
configuration.md; the same reference for C is c-support.md.
| Item | Rule |
|---|---|
| Extension | cpp, cc, cxx, c++, cppm, ixx are sources; h, hh, hpp, hxx, h++, ipp, inl, tpp, tcc are headers |
.h |
belongs to whichever language includes it; read on its own it is C++, because that search list is the wider one |
-std= |
wins over the extension: c++11…c++23 and gnu++… are C++; c… is C |
| Range of standards | C++11 and later (there is no separate C++98/03 mode; older code is read as C++11 and usually still parses) |
| Indexed extensions | c cc cpp cxx c++ h hh hpp hxx h++ ipp inl tpp tcc cppm ixx, plus anything in [workspace].source_extensions (cuh, cppm) |
| Where files come from | compile_commands.json → .cppls.toml → the project scan; an #include no search path can place falls back to matching the tail of its path against the tree |
| CUDA | .cu/.cuh files and an nvcc build are read as CUDA; [compile.cuda] turns that on without a database |
Each construct is a feature bit opened by -std= or [compile].standard.
| Standard | Constructs accepted |
|---|---|
| C++11 | raw strings R"(…)", auto, lambdas, rvalue references &&, variadic templates, alias templates using, [[attribute]], static_assert, alignas/alignof, thread_local, nullptr, enum class, range for |
| C++14 | generic lambdas (auto parameters), variable templates, binary literals, digit separators ', return type deduction |
| C++17 | structured bindings, if constexpr, fold expressions, inline variables, class template argument deduction (CTAD), nested namespaces a::b::c, [[maybe_unused]]/[[nodiscard]]/[[fallthrough]] |
| C++20 | concepts and requires, modules (module/import/export), coroutines (co_await/co_yield/co_return, operator co_await), designated initialisers, the three-way comparison <=>, using enum, char8_t, consteval, constinit |
| C++23 | deducing this (explicit object parameters), if consteval, multidimensional subscript a[i, j], static operator()/operator[], #elifdef/#elifndef |
| C++26 | static reflection (^^T, [: r :], the three splice positions), contracts (pre(…), post(…), contract_assert(…)) — below |
| GNU extensions | statement expressions, __typeof__, __attribute__, __builtin_*, asm, __restrict__ |
| MSVC extensions | __declspec(...), __int64, __try/__except/__finally, __cdecl/__stdcall, macro prefixes such as _STD_BEGIN/_STD_END |
| CUDA | __global__/__device__/__host__, the <<<…>>> launch syntax, built-in vector types such as float4 and dim3 |
#embed and the C++26 library facilities are not supported; both are read as C++23 text. The two C++26 language
features this server does read are parsed unconditionally, because no earlier standard gives their tokens a
meaning — a file that writes them is being read as C++26 whatever -std= says.
| Form | What is read |
|---|---|
^^T, ^^Widget::x, ^^std::array |
a ReflectExpr — a value of type std::meta::info representing the operand (a type-id, a template or concept name, or an id-expression) |
[: r :] |
a SpliceExpr in expression position |
typename [: r :] |
a type splice, in the TypenameType node a dependent type-name uses |
typename [: r :]{…}, typename [: r :](…) |
the same type splice in expression position — a temporary or a functional cast of the reflected type |
typename [: r :]<int> |
a splice specialization: the type splice with template arguments |
d.[: r :], p->[: r :] |
a member splice; it has no written name, so member lookup and the undefined-field check ask nothing about it |
template for (constexpr auto e : r) … |
the expansion statement (P1306): its node is ExpansionStat, and the loop inside it is the ordinary RangeForStat |
^^:: |
a reflection of the global namespace — the form libstdc++s own ` writes |
^^ as a token |
one token, not two ^: the language has no unary ^ to pair them with, and ^ alone is still a Clang block literal |
The type of every reflection expression is std::meta::info, whichever operand it takes — the one expression in the
language whose type does not depend on what it names. What is not there is the library and the evaluation:
std::meta's metafunctions (members_of, type_of, …) are ordinary undeclared names unless a project supplies a
<meta> header, and a splice's type is the entity the reflection names, which this layer does not compute — so
auto x = [: r :]; leaves x's type unknown rather than guessing. crates/cpp_parser/tests/reflection.rs
pins the syntax and crates/cpp_code_analysis/tests/reflection.rs
pins the absence of false diagnostics.
Verified against a compiler. GCC 16.2 (C:\Users\zc\Desktop\mingw\w64devkit, -std=c++26 -freflection, libstdc++'s
own <meta>) accepts every form above, and this server's parser accepts the same files with no syntax error:
| Fixture | GCC 16.2 | this parser |
|---|---|---|
constexpr auto r = ^^Widget; |
ok | 0 errors |
constexpr auto r = ^^Widget::x; |
ok | 0 errors |
constexpr auto r = ^^Box<int>; |
ok | 0 errors |
constexpr auto r = ^^decltype(x); |
ok | 0 errors |
constexpr auto r = ^^::Widget; |
ok | 0 errors |
using W = typename [: r :]; |
ok | 0 errors |
using B = typename [: r :]<int>; |
ok | 0 errors |
Widget w; w.[: r :] |
ok | 0 errors |
typename [: ^^Widget :]{5} (a temporary) |
ok | 0 errors — added after this check found the parser rejecting it |
template for (constexpr auto e : enumerators) { … } |
ok | 0 errors — added after this check found the template token left flat with no error reported |
^^3 |
error: ^^ cannot be applied to this operand |
0 errors (the grammar has no opinion; the analysis does not check operands) |
**The compiler's own is read only when the configuration says-freflection.** libstdc++ wraps the whole header in #if __glibcxx_reflection >= 202603L, which bits/version.hcomputes from__cplusplus > 202302L &&
__cpp_impl_reflection >= 202603L; a compile database that carries -freflection(CMake does, seeC:\Users\zc\Desktop\cpp) makes this layer define __cpp_impl_reflection 202603L, and the metafunctions become resolvable. Without the flag the header's body is a condition the analysis cannot decide, so it is kept out —
std::meta then has no declaration to hover, which reads as "the analysis cannot find std::meta" rather than
"reflection is off".
GCC also confirms the one type this analysis answers: static_assert(std::is_same_v<decltype(^^Widget), std::meta::info>)
compiles, so "a reflection has type std::meta::info" is the implementation's own statement and not a reading of the
proposal.
| Form | Where | What is read |
|---|---|---|
pre(cond) |
after a function's parameter list and qualifiers, before the body | a ContractSpecifier — the caller's obligation |
post(cond) |
the same place | a ContractSpecifier — the function's guarantee |
post(name: cond) |
the same place | a postcondition that names the return value; the introducer is optional (a void function has no result to name) |
contract_assert(cond); |
a statement in a body | a ContractAssertStat — and it is a full keyword |
Both positions are accepted on function declarations, function definitions, constructors (before the
member-initializer list: X(int v) pre(v > 0) : i(v) {}) and lambdas ([](int x) pre(x > 0) { … }). Attributes are
read where the standard allows them, on either side of the parenthesis: pre [[likely]] (x >= 0),
post(r [[maybe_unused]] : r >= 0). The predicate is an ordinary expression, so a call, a comparison, a conditional
or a fold all parse.
pre and post are context-sensitive keywords, so int pre = 42;, void post() {} and a member named post
keep parsing; contract_assert is a full keyword (unlike them), because assert is a macro and the standard needed
a spelling that could not collide with one.
What is not there is the semantics: no contract is evaluated, no violation mode is modelled, and the standard's
ill-formed cases — a contract on a virtual function, or on the first declaration of a = defaulted function — are
not diagnosed. The result name in post(r: …) is read as syntax and is not bound as a variable, so the predicate's
type is not checked against it. crates/cpp_parser/tests/contracts.rs
pins the syntax.
| Ability | What it does |
|---|---|
#include |
angle and quote forms; search paths from the database, the configuration or the toolchain; a header is resolved against the C++ list, with tail matching as the fallback |
#define / #undef |
object-like and function-like, variadic, #, ##, __VA_OPT__; #undef is part of the history |
| Conditionals | #if/#elif/#else/#endif, #ifdef/#ifndef, C++23's #elifdef/#elifndef; expressions are evaluated as integer constants |
| Modules | module, import, export and export module are parsed and indexed as declarations |
| Predefined macros | __cplusplus follows the selected standard; the toolchain's markers (_MSC_VER, _WIN32, __GNUC__, __clang__) follow the compiler |
| Macro environment | the set of macros visible at each position is recorded, and hover, completion, references and rename ask it rather than searching the text |
This is the "understands C++" layer, and the base every completion, hover and check stands on.
| Ability | What it answers |
|---|---|
| Scopes | translation unit, namespaces (anonymous, inline, and aliases — namespace views = ranges::views; resolves to the scope it names), classes/structs/unions/enums (with anonymous-member promotion), functions, blocks, lambdas, try/catch, for init-statements, structured bindings |
| Types | built-in types, pointers/references (lvalue and rvalue)/arrays/function pointers, const/volatile, typedef/alias templates, class template specialisations, decltype, auto, char8_t |
| Template deduction | function template argument deduction (T, const T&, T*, T[], a one-level template-id), explicit template arguments, parameter packs, partial ordering (the more specialised wins), substitution into a class template's members |
| Overload resolution | viable candidates, then a per-argument rank (Exact / Promotion / Conversion / Unranked); the template-versus-ordinary tie-break; a = deleted candidate is never selected; several declarations of one name are chosen between by the arguments |
| Type inference | auto (including auto&&/auto*), decltype, function return types, the return type of factories such as std::make_unique |
| Members | class member lists (inherited, access-controlled, static, using declarations); a class template's members substituted with the arguments; the -> of unique_ptr/shared_ptr and kin follows element_type |
| Calls | argument count and types, variadic functions, default arguments as viable (a parameter list may be longer than the argument list), return type, the object type of a member call, operator() |
| Concepts | requires expressions, concept definitions and constraint checking (concept-not-satisfied) |
| Coroutines | co_await/co_yield/co_return, promise_type, operator co_await, and a coroutine's return type entering auto deduction |
| Constants | #if, array extents, enumerator values, constexpr conditions (which branch of if constexpr is taken) |
| The index | the cross-file declaration table, the #include graph, member-list caches, the summary cache (.cppls); modifiers (virtual, override, final, constexpr, static, maybe_unused, = delete, …) become bits on the fact at index time |
| Feature | What it gives |
|---|---|
textDocument/completion |
names in scope (locals → class members → namespaces → included headers), the right of ./->/:: by type (a namespace alias offers the names of its target, whether the alias is written here or in a header), macros, keywords, #include file names; with the range to replace, a sortText that is the analysis's own order (a local before a keyword before a name from a header), snippets when the client supports them, and completionItem/resolve for the one item being looked at |
textDocument/hover |
macros first, then the declaration (signature/type/scope), then the type of the expression; the comment next to the declaration comes with it |
textDocument/signatureHelp |
the list of overloads with activeParameter — the position of the argument being typed |
textDocument/definition |
the declaration of a macro, type, function, variable, member or enumerator, across files through the index |
textDocument/implementation |
a virtual function's declaration → its overriders; a function declaration → its definition |
textDocument/typeDefinition |
a variable or auto name → the definition of its type (classes, alias templates) |
textDocument/references |
exact references for macros, including the ones reached under a conditional #include or #if (marked as uncertain); an ordinary name is not answered |
textDocument/rename + prepareRename |
macros only; an ordinary name is refused, because editing half of a name's uses is a broken build written into the user's files |
textDocument/documentHighlight |
occurrences of the same name in the current file |
textDocument/documentSymbol |
the file's own declaration tree (including what #if 0 removes), namespaces and classes nested; a range for the declaration and one for the name |
workspace/symbol |
the declarations the project index holds, by name |
callHierarchy |
incoming and outgoing calls of a function (member functions by the object's type) |
textDocument/prepareTypeHierarchy + typeHierarchy/supertypes/subtypes |
the class hierarchy: the class under the cursor, its bases (resolved through the index, by whole spelling and last segment), and the classes whose bases name it |
textDocument/selectionRange |
the chain of syntax nodes around the cursor |
textDocument/foldingRange |
brace blocks, comments, #region, runs of #include |
textDocument/documentLink |
the path of an #include, as a link to the file |
textDocument/inlayHint |
parameter names at call sites |
textDocument/codeAction |
source.include: an unresolved name → an edit adding the header that declares it, chosen by the include-visibility rule; source.create-definition: a member function declared with no definition → the Ret Class::method(params) const { } written at the end of the paired .cpp, or of this file when there is no pair (always this file for a template class, whose members cannot be defined in a .cpp); source.implement-pure-virtuals: a derived class that has not implemented all of its bases pure virtuals → every missing Ret Class::method(params) const { }` in one edit |
textDocument/semanticTokens |
the standard types plus decorator (Doxygen commands) and the custom variableTemplate; inside string literals, std::format/fmt's {} fields and the printf family (formatSpecifier, formatArgument, formatConversion, formatEscape) |
textDocument/diagnostic |
pull mode: syntax errors plus the rules in §6 |
textDocument/formatting, rangeFormatting, willSaveWaitUntil |
whole-file and range formatting, reading the common subset of .clang-format (indent, column limit, brace style, short blocks on one line, …) |
workspace/didChangeConfiguration |
.cppls.toml and editor settings — see configuration.md |
Forty-two rules, each with a fixed severity; [diagnostics].disable turns one off by name.
| Code | Severity | Meaning |
|---|---|---|
syntax-error |
Error | the text does not parse |
param-type-check |
Error | an argument's type cannot go to the parameter |
assign-type-check |
Error | an assignment or initialisation does not fit the type |
undefined-field |
Error | the member is not in the type (a class template's member list is a pattern and is skipped) |
undefined-name |
Error | the name is not declared |
redefinition |
Error | the same entity is defined twice in one scope |
argument-count |
Error | the call passes the wrong number of arguments |
return-type-mismatch |
Error | a return does not fit the declared type |
dangling-return |
Error | a reference, pointer or view of something that dies when the function returns — a local, a temporary, or a pointer a local handed out |
dangling-field |
Error | a view, reference or pointer member initialised from a by-value parameter or a temporary, which do not outlive the object |
use-after-free |
Error | a pointer read after the memory it refers to was released — by delete/free/destroy_at/reset(), or by a call to a function whose fact says it frees that argument |
ue-naming |
Hint | Unreal Engine's prefix conventions — UCLASS → U/A, USTRUCT → F, UENUM → E, a boolean member → b… — UE ([unreal] enable) |
ue-generate-body |
Error | a UCLASS/USTRUCT whose body writes no GENERATED_BODY() — UE ([unreal] enable) |
ue-generated-header |
Error | a file with reflection annotations and no *.generated.h include, or one that is not the last include — UE ([unreal] enable) |
ue-macro-scope |
Warning | a UPROPERTY/UFUNCTION on a local, a parameter or a file-scope declaration — the build tool reflects class members only — UE ([unreal] enable) |
ue-specifier |
Warning | a specifier the engine does not know, which the build tool silently ignores — UE ([unreal] enable) |
include-not-found |
Error | the #include was not found |
access-invisible |
Error | a member reached from somewhere that may not reach it |
missing-return |
Error | a function that promises a value can reach its end |
concept-not-satisfied |
Error | a constraint is not met |
contract-on-virtual-function |
Error | a contract specifier on a virtual function (C++26, P2900; C++29 allows it) |
contract-on-defaulted-function |
Error | a contract specifier on a function defaulted on its first declaration (C++26, P2900) |
override-nonexistent |
Error | override on a function that no base class declares, or outside a class definition |
final-misplaced |
Error | final on a function that is neither virtual nor overriding one — which the standard does not allow |
final-class-derived |
Error | a class deriving from a class declared final |
override-of-final |
Error | an override of a function the base declares final |
abstract-class-instantiated |
Error | a by-value object whose class still has an unoverridden = 0 virtual function |
pure-virtual-not-overridden |
Warning | a class that overrides some of its base's pure virtual functions and leaves the rest — it is still abstract |
macro-redefined |
Warning | the same macro is redefined with a different body |
unnecessary-branch |
Warning | a branch that cannot run (if constexpr/if consteval are not reported) |
assignment-in-condition |
Warning | an assignment inside a condition |
c-style-cast |
Warning | a C-style cast (a (void) cast is not reported) |
macro-parentheses |
Warning | a macro body without parentheses of its own |
unused |
Hint | a name nothing reads (tag: Unnecessary; [[maybe_unused]] and RAII guards are not reported) |
braces-around-statements |
Hint | a single-statement branch without braces |
use-nullptr |
Hint | 0/NULL used as a null pointer |
use-override |
Hint | an overriding function without override/final |
simplify-boolean-expr |
Hint | a boolean expression that can be simplified |
else-after-return |
Hint | an else after a return (if constexpr/if consteval are not reported) |
use-auto |
Hint | a declaration that could be written auto |
implicit-bool-conversion |
Hint | an arithmetic value converted to bool |
module-note |
Information | a note about a module's position (not a complaint, no squiggle) |