Skip to content

Add Unreal Engine 5 fixture and tests for C/C++ (clangd)#1564

Open
Kiborgik wants to merge 2 commits into
oraios:mainfrom
Kiborgik:pr/ue5-fixture
Open

Add Unreal Engine 5 fixture and tests for C/C++ (clangd)#1564
Kiborgik wants to merge 2 commits into
oraios:mainfrom
Kiborgik:pr/ue5-fixture

Conversation

@Kiborgik

@Kiborgik Kiborgik commented Jun 10, 2026

Copy link
Copy Markdown

Adds an Unreal Engine 5 fixture and test suite for the existing C/C++ (clangd) support, plus a setup guide for UE projects. This follows the pattern of #987 (cpp fixture and setup guide) as an isolated addition along existing lines; everything in the diff is test resources and documentation.

Why

UE game code is written against a macro reflection layer (UCLASS, UFUNCTION, UPROPERTY, GENERATED_BODY) and ships generated reflection files that repeat every identifier from the hand-written sources. The current cpp fixture does not exercise this shape, so regressions in macro handling or generated-file leakage would go unnoticed.

Contents

  • A self-contained UE fixture under test/resources/repos/cpp/ue_test_repo/ with its own compile_commands.json: a small game module, stub engine headers mirroring UE 5.7's ObjectMacros.h (the annotation macros expand to nothing in real UE compilation too, only UnrealHeaderTool parses them), and UnrealHeaderTool-style generated files that intentionally repeat the hand-written identifiers. The generated files act as a honeypot: a text search would surface them, a symbol-level tool must not.
  • test/solidlsp/cpp/test_cpp_unreal.py with 12 tests under the existing cpp marker, running clangd over the fixture through a module-scoped session. They assert concrete symbol names and locations: UCLASS/USTRUCT types with their UFUNCTION/UPROPERTY members, UENUM enumerators, UINTERFACE pairs, macro-manufactured delegate types and log categories, cross-file references, go-to-definition, and a rename WorkspaceEdit. Negative assertions verify that nothing resolves into Intermediate/.
  • Two document-symbol helpers in test/solidlsp/conftest.py, shared rather than private to one module.
  • docs/03-special-guides/unreal_engine_setup_guide_for_serena.md covering the two UnrealBuildTool routes to a compile_commands.json, recommended ignored_paths, known behavior, and troubleshooting. Linked from the C/C++ setup guide and the languages page.
  • CHANGELOG entry, and a codespell allowance for the UE abbreviation.

Why a separate repository directory

The first version of this PR placed the fixture inside the shared test_repo, which made its stub headers the first standalone headers that a cpp_ccls session encounters. That surfaced two problems on CI: ccls answers documentSymbol only for files present in the compilation database, and worse, ccls 0.20240202 (the build that Ubuntu and Homebrew ship) crashes intermittently when its session covers these headers, leaving the run hanging on a dead connection. With the fixture in its own repository directory the shared test_repo and test_cpp_basic.py stay exactly as they are on main, ccls never encounters the UE files, and clangd remains the backend the setup guide documents for UE projects. I can share the reproduction details if useful.

Known limitation

Blueprint and reflection-driven references are invisible to any LSP, so renames cannot update them. This is a property of UE, not of clangd, and the guide says so explicitly.

Testing

Local run (Windows 11, clangd 19.1.2 auto-downloaded by Serena):

$ uv run pytest test/solidlsp/cpp/test_cpp_unreal.py -v
test_cpp_unreal.py::TestClangdUnrealEngine::test_symbol_tree_contains_reflected_types PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_document_symbols_show_uclass_members PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_references_resolve_to_handwritten_sources PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_definition_resolves_to_source_not_generated PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_symbol_locations_are_in_handwritten_sources PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_uenum_and_enumerators_visible PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_macro_generated_delegate_type_resolves_to_source PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_interface_pattern_symbols PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_log_category_symbol_in_source PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_nondynamic_delegate_type_in_source PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_character_members_with_ue_types_visible PASSED
test_cpp_unreal.py::TestClangdUnrealEngine::test_rename_edit_targets_only_source_files PASSED
======================== 12 passed, 1 warning in 3.61s ========================

$ uv run pytest test/solidlsp/cpp -m cpp -q
36 passed, 1 xfailed, 1 warning in 10.76s

The xfailed test is the pre-existing test_find_references_in_newly_written_file, unchanged by this PR. Lint, type-check, codespell, and the docs build are clean locally.

The full cpp suite, clangd and ccls both, also passed three consecutive runs in an Ubuntu 24.04 container with apt ccls (40 passed each), which is the environment where the earlier layout hung.

Beyond the stub fixture, the same setup was validated against a real UE 5.7.4 commercial-scale project (1099 translation units, GAS, UMG, Niagara, online subsystems, real engine headers). A harness drove the full tool surface through Serena's agent layer, 40 checks passing: symbol overviews of UCLASS headers, scoped find_symbol, declarations, implementations, cross-file references, go-to-definition, rename with call-site verification, symbol-level edits, and the project's CQTest-based automation test sources, where TEST_CLASS-manufactured classes appear as symbols. All symbol results resolved exclusively to hand-written sources.

Happy to address any feedback.

Kiborgik added 2 commits June 10, 2026 15:47
UE game code is written against a macro reflection layer (UCLASS,
UFUNCTION, UPROPERTY, GENERATED_BODY) and ships generated reflection
files that repeat every identifier from the hand-written sources. The
fixture under test/resources/repos/cpp/test_repo/UE/ mirrors that shape
with stub engine headers and UnrealHeaderTool-style generated files.
Twelve tests verify that symbols, references, definitions, and rename
edits resolve to hand-written sources and never to generated files.

Also adds an Unreal Engine setup guide linked from the C/C++ guide and
the languages page, a CHANGELOG entry, shared document-symbol test
helpers, and a codespell allowance for the UE abbreviation.
The UE stub headers were the first standalone headers in the shared
test repo. ccls 0.20240202, the build that Ubuntu and Homebrew ship,
crashes intermittently when an interactive session covers them, and
the crash leaves pytest hanging on the dead connection. Upstream main
is stable in the same container and standalone batch indexing of the
fixture is clean, so the trigger is the fixture inside the shared repo.

The fixture moves to test/resources/repos/cpp/ue_test_repo with its
own compile_commands.json and a module-scoped clangd session, since
clangd is the supported backend for Unreal Engine projects.
test_cpp_basic.py and the shared compile_commands.json return to
their upstream state. The symbol-location test no longer depends on
directory traversal order, which is what broke it on macOS: generated
headers contain same-named forward declarations, as real
UnrealHeaderTool output does.

The full cpp suite passed three consecutive container runs on Ubuntu
24.04 with apt ccls after the move; the previous layout hung two of
three runs.
@Kiborgik

Copy link
Copy Markdown
Author

CI caught two real issues: a directory-traversal-order dependence in one of the new tests (macOS), and an intermittent crash of ccls 0.20240202 when its session covers the new standalone stub headers, which left the Ubuntu job hanging. The fixture now lives in its own repository directory served by clangd only, the shared test_repo and test_cpp_basic.py are back to their state on main, and the full cpp suite passed three consecutive runs in an Ubuntu 24.04 container with apt ccls. Reproduction details available if useful.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant