## Summary Initial integration of `CompileGraph` (#375) into `MasterServer`, enabling basic end-to-end C++20 module support: on-demand PCM building, dependency-ordered compilation, cascade invalidation on save, and diagnostic integration. This is a **first-pass implementation** — the core pipeline works, but there are known areas for follow-up: - PCM files go to system temp dir instead of `.clice/cache/`; no disk cleanup on invalidation - `run_build_drain` scans imports itself rather than delegating fully to CompileGraph - No incremental/partial rebuild (full PCM rebuild on any change) - Cycle detection is tested at unit level but integration-level coverage is minimal ## Changes ### Module dependency compilation (`master_server.cpp`) Before sending a file to the stateful worker, `run_build_drain` now: 1. Scans imports via `scan_precise()` to discover module dependencies 2. Compiles each dep through `compile_graph->compile()`, which recursively builds transitive PCMs 3. Handles implementation units — `module M;` implicitly needs the interface PCM 4. Passes all built PCMs to the stateful worker, excluding the file's own PCM 5. Skips compile on dep failure and resets `build_running` / `drain_scheduled` 6. Re-lookups iterators after `co_await` to avoid use-after-invalidation ### Cascade invalidation (`didSave` / `didClose`) - `didSave`: calls `compile_graph->update()` to mark transitive dependents dirty, removes stale PCM paths, schedules rebuilds for open dirtied files - `didClose`: cancels in-flight compilations for the closed file ### Other fixes in this PR - Debounce timers switched to `shared_ptr` to prevent use-after-free when `didClose` destroys the timer mid-wait - `fill_compile_args` returns `bool`; callers handle empty CDB gracefully - Adapt all `PositionMapper` call sites to the new `optional` return API from eventide ## Test plan - [x] 25 C++ unit tests for CompileGraph (cycles, partial failure, cancel, update, empty graph) - [x] 24 C++ integration tests with real clang PCM compilation - [x] 3 worker-level module tests (BuildPCM, PCM-dependent compile, multi-module) - [x] 26 Python LSP integration tests (single module through circular deps, hover, error diagnostics) - [x] 371 unit tests + 54 integration tests pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
100 lines
2.2 KiB
C++
100 lines
2.2 KiB
C++
#include <vector>
|
|
|
|
#include "test/test.h"
|
|
#include "test/tester.h"
|
|
#include "feature/feature.h"
|
|
#include "support/filesystem.h"
|
|
|
|
namespace clice::testing {
|
|
|
|
namespace {
|
|
|
|
namespace protocol = eventide::ipc::protocol;
|
|
|
|
TEST_SUITE(DocumentLink) {
|
|
|
|
Tester tester;
|
|
std::vector<protocol::DocumentLink> links;
|
|
|
|
void run(llvm::StringRef source) {
|
|
tester.clear();
|
|
tester.add_files("main.cpp", source);
|
|
ASSERT_TRUE(tester.compile());
|
|
links = feature::document_links(*tester.unit, feature::PositionEncoding::UTF8);
|
|
}
|
|
|
|
auto to_local_range(const protocol::Range& range) -> LocalSourceRange {
|
|
feature::PositionMapper converter(tester.unit->interested_content(),
|
|
feature::PositionEncoding::UTF8);
|
|
return LocalSourceRange(*converter.to_offset(range.start), *converter.to_offset(range.end));
|
|
}
|
|
|
|
void expect_link(std::size_t index, llvm::StringRef name, llvm::StringRef path) {
|
|
auto& link = links[index];
|
|
auto expected = tester.range(name, "main.cpp");
|
|
auto actual = to_local_range(link.range);
|
|
|
|
ASSERT_EQ(actual.begin, expected.begin);
|
|
ASSERT_EQ(actual.end, expected.end);
|
|
ASSERT_TRUE(link.target.has_value());
|
|
|
|
llvm::SmallString<128> target(link.target->begin(), link.target->end());
|
|
path::remove_dots(target);
|
|
ASSERT_EQ(target, path);
|
|
}
|
|
|
|
TEST_CASE(Include) {
|
|
run(R"cpp(
|
|
#[test.h]
|
|
|
|
#[pragma_once.h]
|
|
#pragma once
|
|
|
|
#[guard_macro.h]
|
|
#ifndef TEST3_H
|
|
#define TEST3_H
|
|
#endif
|
|
|
|
#[main.cpp]
|
|
#include @0["test.h"$]
|
|
#include @1["test.h"$]
|
|
#include @2["pragma_once.h"$]
|
|
#include @3["pragma_once.h"$]
|
|
#include @4["guard_macro.h"$]
|
|
#include @5["guard_macro.h"$]
|
|
)cpp");
|
|
|
|
ASSERT_EQ(links.size(), 6U);
|
|
expect_link(0, "0", "test.h");
|
|
expect_link(1, "1", "test.h");
|
|
expect_link(2, "2", "pragma_once.h");
|
|
expect_link(3, "3", "pragma_once.h");
|
|
expect_link(4, "4", "guard_macro.h");
|
|
expect_link(5, "5", "guard_macro.h");
|
|
}
|
|
|
|
TEST_CASE(HasInclude) {
|
|
run(R"cpp(
|
|
#[test.h]
|
|
|
|
#[main.cpp]
|
|
#include @0["test.h"]
|
|
|
|
#if __has_include(@1["test.h"])
|
|
#endif
|
|
|
|
#if __has_include("test2.h")
|
|
#endif
|
|
)cpp");
|
|
|
|
ASSERT_EQ(links.size(), 2U);
|
|
expect_link(0, "0", "test.h");
|
|
expect_link(1, "1", "test.h");
|
|
}
|
|
|
|
}; // TEST_SUITE(DocumentLink)
|
|
|
|
} // namespace
|
|
|
|
} // namespace clice::testing
|