Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions cpp/libclang/docs/ast-traversal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<!-- ----------------------------------------------------------------------------
Copyright (c) 2026 Contributors to the Eclipse Foundation

See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.

This program and the accompanying materials are made available under the
terms of the Apache License Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0

SPDX-License-Identifier: Apache-2.0
----------------------------------------------------------------------------- -->

# libclang C++ source analysis

This document is the entry point for the parser's analysis of a C++ source
file. The parser creates one libclang translation unit for each input file and
walks its AST recursively. It extracts a class-diagram model and a callable
control-flow model from eligible entities.

Detailed extraction contracts are documented separately:

- [Function extraction and control flow](function-extraction.md)
- [Class and enum extraction](type-extraction.md)

## Translation units and the main file

In libclang terminology, the *main file* is the input file of one parse
operation; it is not necessarily the C++ program's `main.cpp`. For example,
when parsing `src/service.cpp`, declarations and definitions in that file are
in the main file, while entities brought in through `#include` are not.

Function extraction keeps only callable definitions from the main file. This
avoids extracting an inline function from a shared header once for every
translation unit that includes it. A header-only function is extracted when
that header is itself an input file.

## Analysis flow

`Visitor` recursively walks the translation-unit AST. For each entity that is
not filtered out, it dispatches to the relevant specialized visitor:

| Entity kind | Analysis |
| --- | --- |
| `ClassDecl`, `StructDecl`, `ClassTemplate`, and `ClassTemplatePartialSpecialization` | Extract class/struct entities, members, aliases, bases, and relationship inputs. |
| `EnumDecl` | Extract enum entities and literals. |
| `FunctionDecl` and `Method` | Extract callable definitions and their body control flow. |

After traversal, class relationship resolution uses the collected base,
variable, and method type information to populate the class-diagram
relationships.

## Source filtering

Before dispatch, the parser omits entities located in system or external paths,
or entities in excluded namespaces such as `std`. Namespace cursors are
traversal containers; visitors derive namespace and type ownership from each
entity's semantic-parent chain rather than retaining mutable namespace state.

This filtering is distinct from the main-file rule: source filtering controls
whether an entity belongs in the parsed model at all, while the main-file rule
prevents duplicated callable definitions from project headers.
138 changes: 138 additions & 0 deletions cpp/libclang/docs/function-extraction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
<!-- ----------------------------------------------------------------------------
Copyright (c) 2026 Contributors to the Eclipse Foundation

See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.

This program and the accompanying materials are made available under the
terms of the Apache License Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0

SPDX-License-Identifier: Apache-2.0
----------------------------------------------------------------------------- -->

# libclang function extraction and control flow

This document records the current contract for extracting C++ callable
definitions and their control flow. It describes the assumptions made by
`FunctionVisitor`, rather than the complete libclang API. For the overall
per-source-file analysis flow, see [libclang C++ source analysis](ast-traversal.md).

## Function extraction

A callable becomes a `FunctionDef` only when all of the following hold:

1. it is located in the current translation unit's main file;
2. a `FunctionId` can be derived;
3. its cursor kind maps to a supported `FunctionKind`;
4. it owns a direct `CompoundStmt` body.

A declaration-only function therefore does not produce a `FunctionDef`. This
is important because an empty function body and the absence of any function
body have different meanings.

The function visitor first derives the function identity. Subsequent diagnostic
messages can then include a qualified name rather than only an unqualified
cursor spelling.

### Function body processing

A function cursor's direct children include parameter declarations and, for a
normal definition, a `CompoundStmt`. The visitor locates that `CompoundStmt`
and passes it to `process_scope`.

`process_scope` processes direct statements in source order:

- `IfStmt` is represented as one `BodyItem::Branch` with ordered cases. A case
has an optional `GuardExpression`, a body, and a source location; a final
`else` case has no guard. When libclang exposes supported unary and binary
cursor layouts, a guard preserves `&&`, `||`, and `!` as a tree so condition
calls retain their C++ short-circuit execution prerequisites. Other
expressions remain source-backed opaque leaves. An `else if` chain is
flattened into additional cases. Unsupported `IfStmt` child layouts use a
conservative fallback that preserves reachable calls without inventing an
unreliable branch shape;
- `ForStmt`, `WhileStmt`, and `DoStmt` are represented as `BodyItem::Loop`
with a typed `LoopKind`;
- `CallExpr` is represented as `BodyItem::Call` when its target has a different
owner;
- other non-control-flow statements are searched recursively for calls.

Calls nested within another call are emitted before their enclosing call. This
is structural nesting order only: the visitor does not claim an evaluation
order between sibling C++ call arguments.

## Callable scope and identity

A `FunctionId` contains a callable name and a structured `Scope`.

```text
FunctionId = Scope + function name
```

`Scope` distinguishes:

- `Global` for global functions;
- `Namespace(path)` for namespace functions;
- `Type { namespace, type_path }` for member functions.

Keeping the scope structured prevents a namespace and a type with the same
spelling from being treated as the same owner. It also preserves nested types.
For example:

```cpp
namespace app {
class Outer {
public:
class Inner {
public:
void run();
};
};
}
```

is represented conceptually as:

```text
Scope::Type {
namespace: ["app"],
type_path: ["Outer", "Inner"],
}
FunctionId: app::Outer::Inner::run
```

The scope adapter extracts this information from semantic parents. It keeps
namespace paths distinct from type paths because they have different C++
semantics.

### Overloads

The current `FunctionId` does not include parameter types. Consequently,
overloads in the same scope currently share an identity for call-resolution
purposes. Do not use `FunctionId` as a single-value de-duplication key until a
signature is added to the model.

## Supported cursor kinds

The top-level visitor currently dispatches these cursor kinds to
`FunctionVisitor`:

| libclang cursor kind | Function kind |
| --- | --- |
| `FunctionDecl` | `Free` |
| `Method` | `Method` or `StaticMethod` |

C++ member operator overloads such as `operator+` and `operator[]` are normally
reported as `Method`; the current model does not use a distinct operator-method
kind.

`FunctionVisitor` has internal kind mappings for `Constructor`, `Destructor`,
and `ConversionFunction`, but the top-level visitor currently logs and ignores
those cursor kinds. Therefore they do not currently produce `FunctionDef`
entries. A conversion operator such as `operator bool()` is a
`ConversionFunction` and is distinct from a normal operator overload.

Function templates are not currently part of function extraction. Class visitor
handling of method templates is independent from extraction of function bodies
and call relationships.
84 changes: 84 additions & 0 deletions cpp/libclang/docs/type-extraction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!-- ----------------------------------------------------------------------------
Copyright (c) 2026 Contributors to the Eclipse Foundation

See the NOTICE file(s) distributed with this work for additional
information regarding copyright ownership.

This program and the accompanying materials are made available under the
terms of the Apache License Version 2.0 which is available at
https://www.apache.org/licenses/LICENSE-2.0

SPDX-License-Identifier: Apache-2.0
----------------------------------------------------------------------------- -->

# libclang class and enum extraction

This document records the current contract for extracting C++ classes, structs,
and enums. It describes the implementation choices in `ClassVisitor` and
`EnumVisitor`, rather than the complete libclang API. For the overall
per-source-file analysis flow, see [libclang C++ source analysis](ast-traversal.md).

## Traversal and source filtering

`Visitor` recursively walks the translation-unit AST and dispatches matching
cursors to specialized visitors:

- `ClassVisitor` handles classes, structs, class templates, and partial class
template specializations;
- `EnumVisitor` handles enums;
- `FunctionVisitor` handles function definitions and control flow, documented
in [function-extraction.md](function-extraction.md).

Namespace nodes are traversal containers. Visitors derive namespaces and type
owners from each entity's semantic-parent chain instead of maintaining mutable
namespace state. Before dispatch, entities in system or external paths and
entities in excluded namespaces such as `std` are omitted.

`Entity::visit_children` is used for direct-child inspection where a visitor
needs control over recursion. The top-level visitor recursively traverses the
translation unit, so nested classes and nested enums are discovered separately.

## Class, struct, interface, and abstract-class extraction

Named `ClassDecl`, `StructDecl`, `ClassTemplate`, and
`ClassTemplatePartialSpecialization` cursors are represented as class-diagram
entities. Anonymous classes and structs are skipped because they have no stable
name. Their resulting entity type is inferred from the cursor kind and member
set as `Struct`, `Class`, `Interface`, or `AbstractClass`.

The classification rules are:

- `Struct`: the source cursor is `StructDecl`;
- `Class`: the cursor is not a struct and has no pure-virtual method;
- `Interface`: the class has at least one pure-virtual method, no data member,
and no concrete non-constructor/destructor method;
- `AbstractClass`: the class has at least one pure-virtual method but does not
meet the `Interface` conditions, because it has a data member or a concrete
non-constructor/destructor method.

For each direct member cursor, `ClassVisitor` currently extracts:

- base specifiers and their resolved types;
- methods, constructors, destructors, and method templates;
- fields and variable declarations;
- `using` aliases and `typedef` declarations.

Method parameter types normally come from libclang's argument list. When that
list is unavailable for a cursor such as `FunctionTemplate`, the visitor falls
back to direct `ParmDecl` children. Template parameters are retained for class
templates, partial specializations, and method templates.

The visitor records intermediate type information for base classes, variables,
and methods. After the translation unit has been traversed, relationship
resolution derives the class-diagram relationships from that collected type
information.

## Enum extraction

Named `EnumDecl` cursors are represented as enum entities. Anonymous enums are
skipped because they have no stable name.

Each direct `EnumConstantDecl` becomes an enum literal with its name, numeric
value, and source location. The visitor reads the enum's underlying type to
choose the unsigned value supplied by libclang for unsigned enums; values are
stored as `i128` so every `u64` and `i64` value can be serialized safely.
Loading