Skip to content

feat: header context protocol — queryContext, currentContext, switchContext#398

Merged
16bit-ykiko merged 11 commits intomainfrom
feat/header-context
Apr 6, 2026
Merged

feat: header context protocol — queryContext, currentContext, switchContext#398
16bit-ykiko merged 11 commits intomainfrom
feat/header-context

Conversation

@16bit-ykiko
Copy link
Copy Markdown
Member

@16bit-ykiko 16bit-ykiko commented Apr 6, 2026

Summary

Add three LSP protocol extensions that allow users to manage compilation contexts for header files and source files with multiple CDB entries.

Protocol extensions (protocol.h)

Command Purpose
clice/queryContext List all possible contexts for a file. Headers → host source files; sources → CDB entries. Paginated (10 per page, offset param).
clice/currentContext Query the active context override for a file (null if default).
clice/switchContext Set the active context, invalidate caches, trigger recompilation.

Header context resolution (master_server.cpp, dependency_graph.cpp)

  • find_host_sources(): BFS the reverse include graph to find source files that transitively include a header
  • find_include_chain(): BFS the forward include graph to find the shortest include chain from host to header
  • resolve_header_context(): walks the include chain, extracts content before each #include directive, concatenates with #line markers into a preamble file (hash-addressed under .clice/header_context/)
  • fill_header_context_args(): uses the host source's CDB entry, replaces source path with header path, injects -include preamble.h

Compilation flow

  • Default: headers compile as standalone files (no context)
  • After switchContext: fill_compile_args checks active_contexts first → uses host's CDB entry + preamble injection
  • Fallback: if no CDB entry and no active context, auto-resolves via resolve_header_context
  • #include directive matching uses precise filename extraction from "..." / <...>, not substring matching

Source file multiple contexts (multi_context workspace)

  • queryContext on a source file returns all CDB entries with distinguishing labels (extracted from -D, -O, -std= flags)

Test data

  • header_context/: non-self-contained 3-level chain (main.cpputils.hinner.h), types.h provides Point struct
  • multi_context/: single source with two CDB entries (-DCONFIG_A, -DCONFIG_B)

Tests (9 integration tests)

  • queryContext returns host sources for headers
  • queryContext returns CDB entries for source files
  • currentContext defaults to null
  • switchContext sets active context, currentContext reflects it
  • Full flow: open → query → switch → hover works in non-self-contained header
  • Deep nested: switchContext + hover on inner.h (3 levels deep)
  • Multiple CDB entries: queryContext returns both CONFIG_A and CONFIG_B

Test plan

  • Unit tests: 465 passed
  • Integration tests: 113 passed (9 new header context tests)
  • Smoke test: 1/1 passed
  • Manual VSCode testing

🤖 Generated with Claude Code

Header files without compile_commands.json entries can now get hover,
completion, diagnostics, and other IDE features by inferring compilation
context from a host source file that includes them.

Implementation:
- Add reverse include map to DependencyGraph (header → includer mapping)
- Add find_host_sources() BFS to find source files that include a header
- Add find_include_chain() to trace the include path from host to target
- Add resolve_header_context() to generate preamble files with #line
  directives preserving correct source locations
- Modify fill_compile_args() to fall back to header context when CDB
  lookup fails: uses host's compile args + -include <preamble>
- Cache preamble files in <cache_dir>/header_context/<hash>.h
- Invalidate header contexts when host file is saved

Tests:
- test_hover_in_header_file: hover on function in header returns result
- test_diagnostics_in_header_file: valid header gets zero diagnostics

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds header-file context resolution: reverse-include graph traversal, host-source discovery, preamble construction and disk caching, compile-argument injection for headers without CDB entries, save-driven cache invalidation, and three LSP extension requests to query/switch/inspect header contexts.

Changes

Cohort / File(s) Summary
Dependency Graph Enhancements
src/syntax/dependency_graph.h, src/syntax/dependency_graph.cpp
Added reverse adjacency (reverse_includes_) via build_reverse_map(), getters (get_includers()), upward host discovery (find_host_sources()), and forward include-path reconstruction (find_include_chain()).
Master Server Header Context
src/server/master_server.h, src/server/master_server.cpp
Added HeaderFileContext struct, caches header_file_contexts and active_contexts, resolve_header_context() implementation that builds/truncates/include-chain preambles, hashes and writes cached preamble files, and revised fill_compile_args() to prefer direct CDB entries or inject -include <preamble> for resolved header contexts. Added didSave-driven invalidation of header contexts and handlers for clice/queryContext, clice/currentContext, clice/switchContext.
Compilation Database Extensions
src/command/command.h, src/command/command.cpp
Added CompilationDatabase::has_entry(llvm::StringRef) to query whether a file has explicit CDB entries.
LSP Protocol Extensions
src/server/protocol.h
Introduced clice::ext types: ContextItem, QueryContextParams/Result, CurrentContextParams/Result, SwitchContextParams/Result for context querying/switching APIs.
Test Data – Header & Multi Contexts
tests/data/header_context/*, tests/data/multi_context/*
Added test workspace files: main.cpp, utils.h, inner.h (header_context) and multi-config main.cpp (multi_context) to exercise include chains and multiple CDB entries.
Tests & Fixtures
tests/integration/test_header_context.py, tests/conftest.py
Added integration tests validating query/current/switch flows and end-to-end compilation/hover diagnostics; extended test fixture to write compile_commands.json for the new test workspaces.
Tests – helpers
tests/data/header_context/*
New small headers/sources used by tests (non-self-contained headers relying on include chain).

Sequence Diagram(s)

sequenceDiagram
    participant Client as LSP Client
    participant Server as MasterServer
    participant CDB as CompilationDatabase
    participant DepGraph as DependencyGraph
    participant Cache as Preamble Cache

    Client->>Server: request fill_compile_args(header_path)
    Server->>CDB: has_entry(header_path)
    alt CDB has entry
        CDB-->>Server: compile_args
        Server-->>Client: compile_args
    else no CDB entry
        Server->>DepGraph: find_host_sources(header_path)
        DepGraph-->>Server: [host_paths]
        Server->>DepGraph: find_include_chain(host_path, header_path)
        DepGraph-->>Server: include_chain
        Server->>Server: build_preamble(include_chain)
        Server->>Server: hash_preamble
        Server->>Cache: write/check preamble/<hash>.h
        Cache-->>Server: preamble_path
        Server->>CDB: get_toolchain(host_path)
        CDB-->>Server: host_compile_args
        Server->>Server: inject -include preamble_path into args
        Server-->>Client: compile_args (with preamble)
    end
Loading
sequenceDiagram
    participant Client as LSP Client
    participant Server as MasterServer
    participant DepGraph as DependencyGraph

    Client->>Server: clice/queryContext(header_uri)
    Server->>DepGraph: find_host_sources(header_path_id)
    DepGraph-->>Server: [host_1, host_2, ...]
    Server-->>Client: QueryContextResult{contexts, total}

    Client->>Server: clice/switchContext(header_uri, context_uri)
    Server->>Server: validate context (CDB entry)
    Server->>Server: update active_contexts, erase header cache, invalidate pch/ast_deps
    Server-->>Client: SwitchContextResult{success:true}

    Client->>Server: textDocument/didSave(host_uri)
    Server->>Server: erase header_file_contexts with host_path_id == saved_path_id
    Server-->>Client: (log + mark ast_dirty for affected docs)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I hopped through includes, one by one,
I stitched the preamble 'til the work was done,
Cached a hash and found a host,
Switched contexts quick—no build is lost!
A happy rabbit for a header well-spun 🎉

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary change: adding header context protocol support with three new LSP extension commands (queryContext, currentContext, switchContext).

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/header-context

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/master_server.cpp`:
- Around line 2195-2209: The current invalidation only checks
header_file_contexts[*].host_path_id and misses cases where the saved file is an
intermediate contributor to a preamble; update the preamble tracking so each
header_file_context records all contributing path IDs (e.g., add a contributors
set/list to the header context structure used by header_file_contexts) and
change the invalidation loop to mark any hdr_ctx whose contributors contain the
saved path_id as stale (and set ast_dirty on documents as before); additionally,
after saves that can affect includes, invoke the workspace include-graph rebuild
(the same logic performed in load_workspace() or a new rebuild_include_graph()
helper) to refresh the reverse-include map so future resolution uses the current
include edges.
- Around line 624-656: The current scan uses line.contains(next_filename) which
can falsely match substrings; instead parse the include directive on each
candidate line (handle both `#include` and "# include" with arbitrary spacing,
angle brackets <> and quoted "..." forms) by extracting the exact include target
token, normalize it, and resolve that token to the intended include ID (next_id)
using the same include-resolution logic/lookup you use elsewhere (do not rely on
substring matching of next_filename); if the resolved ID equals next_id then set
include_line_start and break, otherwise continue scanning—update references to
next_filename/ include_line_start/content_ref/preamble/cur_path/LOG_DEBUG
accordingly so the fallback full-file emission only happens when no real include
resolved to next_id.
- Around line 568-590: The resolve_header_context function currently selects a
host by iterating hosts and only stores host_path_id and chain, then later
re-looks up a compile command with cdb.lookup(host_path).front(), which can
produce a non-deterministic or mismatched compile command; modify the selection
loop in MasterServer::resolve_header_context to also capture and store the
specific compile-command/config returned by cdb.lookup (e.g., the chosen
CdbEntry or CompileCommand object) when you pick host_path_id and chain, and
return that stored compile command inside the resulting HeaderFileContext
instead of performing a fresh cdb.lookup by path later; apply the same change to
the second similar block referenced (lines ~705-727) so both code paths pair the
exact include chain with the exact compile command chosen during selection.
- Around line 730-742: The current logic drops the last positional argument by
position (copy_count) which can remove the wrong token (e.g. the output file)
and leave dangling flags; instead iterate host_ctx.arguments and skip entries
that are actual host source file paths by identity: for each token in
host_ctx.arguments, if it is not a flag (doesn't start with '-') and its file
extension matches known source extensions (e.g. .c, .cpp, .cc, .cxx, .m, .mm,
.C, .c++), do not copy it into arguments; otherwise copy it via
arguments.emplace_back; also ensure you don't accidentally drop flag values
(handle flags like "-o" by treating them as flags whose following token should
be preserved unless that following token itself is a recognized source filename
to remove). This replaces the copy_count/back-based approach and uses
host_ctx.arguments, arguments.emplace_back, and source-extension checks to
identify/removes host source args by identity.

In `@tests/integration/test_header_context.py`:
- Line 22: The assignment to main_uri from client.open_and_wait is unused and
flagged by RUF059; update the binding to a throwaway name (e.g., _main_uri) or
drop the value (e.g., await client.open_and_wait(...)) where main_uri is
assigned (search for client.open_and_wait and the other similar assignment
around line 47) so both unused variables are prefixed with _ or removed to
satisfy the linter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e84cd845-b387-4379-aa62-21883156585d

📥 Commits

Reviewing files that changed from the base of the PR and between e239b0d and ce08a25.

📒 Files selected for processing (8)
  • src/server/master_server.cpp
  • src/server/master_server.h
  • src/syntax/dependency_graph.cpp
  • src/syntax/dependency_graph.h
  • tests/data/header_context/compile_commands.json
  • tests/data/header_context/main.cpp
  • tests/data/header_context/utils.h
  • tests/integration/test_header_context.py

Comment thread src/server/master_server.cpp Outdated
Comment thread src/server/master_server.cpp
Comment thread src/server/master_server.cpp Outdated
Comment thread src/server/master_server.cpp
Comment thread tests/integration/test_header_context.py
- clice/queryContext: returns all possible compilation contexts for a file
  (header files → host source files, source files → CDB entries), paginated
- clice/currentContext: returns the currently active context for a file
- clice/switchContext: switches the active context, invalidates caches, triggers recompile
- Non self-contained header test data (utils.h depends on host's <vector>)
- 5 integration tests covering the full query→switch→verify flow
- CDB lookup helper (command.h/cpp) for querying multiple compilation entries

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@16bit-ykiko 16bit-ykiko changed the title feat: header context — IDE features for header files feat: header context support with queryContext/switchContext protocol Apr 6, 2026
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/master_server.cpp`:
- Around line 2811-2846: The handler for the "clice/switchContext" request
(inside the peer.on_request lambda) currently only checks
cdb.lookup(context_path) but never verifies that the chosen context actually
transitively includes the target header (the file identified by path), so it may
accept unrelated context files; before updating active_contexts, call or
implement a validation that the context source file transitively includes the
header (e.g., reuse existing include-resolution logic or a helper like
resolve_header_context/includes_graph lookup to confirm context_path -> ... ->
path), and if that check fails set result.success = false and return; only
proceed to modify active_contexts, header_file_contexts, pch_states, and mark
documents[].ast_dirty when the include-chain validation succeeds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9e960280-73da-4631-94d7-97b879d469d1

📥 Commits

Reviewing files that changed from the base of the PR and between ce08a25 and de034b3.

📒 Files selected for processing (10)
  • src/command/command.cpp
  • src/command/command.h
  • src/server/master_server.cpp
  • src/server/master_server.h
  • src/server/protocol.h
  • tests/conftest.py
  • tests/data/header_context/compile_commands.json
  • tests/data/header_context/main.cpp
  • tests/data/header_context/utils.h
  • tests/integration/test_header_context.py
✅ Files skipped from review due to trivial changes (3)
  • tests/data/header_context/compile_commands.json
  • tests/conftest.py
  • src/server/protocol.h
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/data/header_context/main.cpp
  • src/server/master_server.h

Comment thread src/server/master_server.cpp
16bit-ykiko and others added 2 commits April 6, 2026 16:14
…ted + multi-context tests

- queryContext now returns compilation contexts (CDB entries) for source files,
  not just header contexts. Labels show distinguishing flags (-D, -O, -std=).
- Handle split args (-D CONFIG_A → -DCONFIG_A) in label generation.
- Add inner.h for deep nested chain: main.cpp → utils.h → inner.h
- Test: deep nested header finds main.cpp as host through 2-level chain
- Test: source file with 2 CDB entries (-DCONFIG_A, -DCONFIG_B) returns both
- Fix hover line number after adding #include "inner.h" to utils.h

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…bels

- Add ast_deps.erase(path_id) in switchContext to prevent stale dependency snapshots
- Remove -I from split-arg handling in CDB label generation (noisy long paths)
- Untrack generated compile_commands.json (conftest generates it dynamically)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (6)
tests/integration/test_header_context.py (1)

35-35: ⚠️ Potential issue | 🟡 Minor

Drop the unused main_uri bindings.

These assignments are still flagged by Ruff (RUF059). Use _main_uri or just await the helper without binding the return value.

Suggested fix
-    main_uri, _ = await client.open_and_wait(workspace / "main.cpp")
+    await client.open_and_wait(workspace / "main.cpp")
@@
-    main_uri, _ = await client.open_and_wait(workspace / "main.cpp")
+    await client.open_and_wait(workspace / "main.cpp")
@@
-    main_uri, _ = await client.open_and_wait(workspace / "main.cpp")
+    await client.open_and_wait(workspace / "main.cpp")

Also applies to: 74-74, 193-193

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_header_context.py` at line 35, The test assigns an
unused return value from client.open_and_wait (e.g., main_uri = await
client.open_and_wait(workspace / "main.cpp")), which triggers RUF059; fix by
removing the binding and either await the call without assignment or name it
_main_uri (use the same approach for the other occurrences of
client.open_and_wait in this file), updating the calls where main_uri is unused
so no unused variable is created.
src/server/master_server.cpp (5)

759-772: ⚠️ Potential issue | 🔴 Critical

Don’t replace the input by dropping the last positional argument.

Common compile commands put output paths after the source file. With something like clang++ -c main.cpp -o main.o, this removes main.o, leaves a dangling -o, and still keeps main.cpp.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 759 - 772, The current logic
decrements copy_count to drop the last non-flag argument which can mistakenly
remove an output filename (e.g., "-o main.o") and leave dangling flags; instead
locate the index of the last non-flag argument in host_ctx.arguments (scan from
host_ctx.arguments.size()-1 backwards until you find an entry that does not
start with "-"), copy all arguments up to but not including that index into
arguments, then append the header path (path) in place of that single positional
argument (or if no positional arg found, simply append path). Update the code
that uses copy_count and arguments.emplace_back(path) so it replaces the
identified positional arg rather than removing the last element unconditionally.

641-656: ⚠️ Potential issue | 🟠 Major

Parse the include directive instead of substring-matching the basename.

line.contains(next_filename) can match comments, string literals, or a different header with the same basename. That truncates the generated preamble at the wrong line and changes the compilation context.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 641 - 656, The current loop uses
line.contains(next_filename) which can match comments or unrelated tokens;
instead parse the `#include` token properly: after detecting
line.starts_with("#include") (or "# include"), extract the include operand (the
token between <...> or "..." ), trimming whitespace and stripping any trailing
comments, then compare the extracted filename (or its basename via
llvm::sys::path::filename / equivalent StringRef splitting) to next_filename for
an exact match; update the include detection in the block around content_ref,
line_start, include_line_start and next_filename so only a true include of the
target file sets include_line_start.

577-607: ⚠️ Potential issue | 🟠 Major

Store the chosen CDB entry with the resolved host.

This still picks the first viable host and later does cdb.lookup(host_path).front(). If the same source has multiple CDB entries, the include chain can be paired with the wrong config, and the advertised open-file/shortest-chain preference is not enforced here.

Also applies to: 748-756

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 577 - 607, The current logic
selects host_path_id and chain but re-looks-up cdb entries later, which can pair
the include chain with the wrong compilation entry when a source has multiple
CDB entries; modify the code in the active-context branch and the hosts fallback
loop to capture and store the selected CDB lookup result alongside host_path_id
and chain (e.g., introduce a chosen_cdb_entry variable and assign it from the
local results when you set host_path_id and chain), use that saved
chosen_cdb_entry later instead of calling cdb.lookup(host_path) again, and apply
the same change to the duplicate block around the other occurrence (the later
748-756 block) so the chosen compile command is the one corresponding to the
resolved host and chain.

2233-2247: ⚠️ Potential issue | 🟠 Major

Invalidate header contexts for every preamble contributor.

The cached preamble is built from the whole include chain, but this only evicts entries whose host_path_id matches the saved file. Saving an intermediate header can therefore keep a stale preamble alive; if include edges changed, the reverse map also stays stale until reload.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 2233 - 2247, The current eviction
only removes header contexts whose hdr_ctx.host_path_id == path_id but must also
evict any header whose cached preamble was contributed to by the saved file;
update the first loop over header_file_contexts to treat a header as stale if
hdr_ctx.host_path_id == path_id OR hdr_ctx.preamble_contributors (or the
equivalent include-contributor set on hdr_ctx) contains path_id, then proceed to
erase from header_file_contexts and mark documents[hdr_id].ast_dirty exactly as
done now; use the existing symbols header_file_contexts, hdr_ctx, host_path_id,
path_id, and preamble_contributors (or the corresponding reverse-include
structure) to locate and change the condition.

2857-2879: ⚠️ Potential issue | 🟠 Major

Reject unrelated context files up front.

This only checks whether context_uri has a CDB entry. If it does not actually include the target header, resolve_header_context() later falls back to some other host, but switchContext still returns success=true and currentContext keeps reporting the requested source.

Suggested fix
             auto context_cdb = cdb.lookup(context_path, {.suppress_logging = true});
             if(context_cdb.empty()) {
                 result.success = false;
                 co_return to_raw(result);
             }
+
+            auto chain = dependency_graph.find_include_chain(context_path_id, path_id);
+            if(chain.empty()) {
+                result.success = false;
+                co_return to_raw(result);
+            }
 
             // Set active context and invalidate cached header context so
             // resolve_header_context will pick the new host on next compile.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 2857 - 2879, The code only
verifies the requested context exists but not that it actually provides the
target header; before setting active_contexts and returning success, perform a
second check that the selected context (context_path / context_path_id) contains
the header identified by path_id (i.e. that a lookup for the header in that CDB
is non-empty) — if that header lookup fails, set result.success = false and
return; keep references to context_path, context_path_id, path_id and the later
resolve_header_context() behavior so the guard runs here in switchContext (or
the function wrapping this logic) to prevent reporting a currentContext that
does not actually serve the requested header.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/server/master_server.cpp`:
- Around line 707-716: The current fast-path returns when cdb.has_entry(path)
causing clice/switchContext changes in active_contexts to be ignored; modify the
logic in master_server.cpp so that before using cdb.lookup(path) and returning
early (the block around cdb.has_entry and cdb.lookup), you check active_contexts
for an active override for this path and if present use that context (directory
and arguments) instead of the direct CDB result; only fall back to cdb.lookup
when no active_contexts entry applies, and remove the unconditional early return
so switchContext can take effect.
- Around line 2755-2818: The code reads a client-controlled params.offset into
offset_val and then uses it as the start of the loop (for(int i =
offset_val...)) which can be negative and cause out-of-bounds access; fix by
validating/clamping offset_val from params.offset (e.g., set offset_val =
std::max(0, params.offset.value_or(0)) or return an error for negative offsets)
before computing end and iterating, ensuring the loop start is non-negative and
consistent with result.total and page_size when filling result.contexts (refer
to symbols params.offset, offset_val, page_size, all_items, result and the
for(int i = offset_val...) loop).

In `@tests/integration/test_header_context.py`:
- Around line 227-230: The assertion generator uses a single-letter variable `l`
which triggers Ruff E741; update both assertions to use a clearer name like
`label` (e.g., change `any("CONFIG_A" in l for l in labels)` and `any("CONFIG_B"
in l for l in labels)` to `any("CONFIG_A" in label for label in labels)` and
`any("CONFIG_B" in label for label in labels)`), keeping the surrounding
variables `labels`, `_get`, and `contexts` unchanged.

---

Duplicate comments:
In `@src/server/master_server.cpp`:
- Around line 759-772: The current logic decrements copy_count to drop the last
non-flag argument which can mistakenly remove an output filename (e.g., "-o
main.o") and leave dangling flags; instead locate the index of the last non-flag
argument in host_ctx.arguments (scan from host_ctx.arguments.size()-1 backwards
until you find an entry that does not start with "-"), copy all arguments up to
but not including that index into arguments, then append the header path (path)
in place of that single positional argument (or if no positional arg found,
simply append path). Update the code that uses copy_count and
arguments.emplace_back(path) so it replaces the identified positional arg rather
than removing the last element unconditionally.
- Around line 641-656: The current loop uses line.contains(next_filename) which
can match comments or unrelated tokens; instead parse the `#include` token
properly: after detecting line.starts_with("#include") (or "# include"), extract
the include operand (the token between <...> or "..." ), trimming whitespace and
stripping any trailing comments, then compare the extracted filename (or its
basename via llvm::sys::path::filename / equivalent StringRef splitting) to
next_filename for an exact match; update the include detection in the block
around content_ref, line_start, include_line_start and next_filename so only a
true include of the target file sets include_line_start.
- Around line 577-607: The current logic selects host_path_id and chain but
re-looks-up cdb entries later, which can pair the include chain with the wrong
compilation entry when a source has multiple CDB entries; modify the code in the
active-context branch and the hosts fallback loop to capture and store the
selected CDB lookup result alongside host_path_id and chain (e.g., introduce a
chosen_cdb_entry variable and assign it from the local results when you set
host_path_id and chain), use that saved chosen_cdb_entry later instead of
calling cdb.lookup(host_path) again, and apply the same change to the duplicate
block around the other occurrence (the later 748-756 block) so the chosen
compile command is the one corresponding to the resolved host and chain.
- Around line 2233-2247: The current eviction only removes header contexts whose
hdr_ctx.host_path_id == path_id but must also evict any header whose cached
preamble was contributed to by the saved file; update the first loop over
header_file_contexts to treat a header as stale if hdr_ctx.host_path_id ==
path_id OR hdr_ctx.preamble_contributors (or the equivalent include-contributor
set on hdr_ctx) contains path_id, then proceed to erase from
header_file_contexts and mark documents[hdr_id].ast_dirty exactly as done now;
use the existing symbols header_file_contexts, hdr_ctx, host_path_id, path_id,
and preamble_contributors (or the corresponding reverse-include structure) to
locate and change the condition.
- Around line 2857-2879: The code only verifies the requested context exists but
not that it actually provides the target header; before setting active_contexts
and returning success, perform a second check that the selected context
(context_path / context_path_id) contains the header identified by path_id (i.e.
that a lookup for the header in that CDB is non-empty) — if that header lookup
fails, set result.success = false and return; keep references to context_path,
context_path_id, path_id and the later resolve_header_context() behavior so the
guard runs here in switchContext (or the function wrapping this logic) to
prevent reporting a currentContext that does not actually serve the requested
header.

In `@tests/integration/test_header_context.py`:
- Line 35: The test assigns an unused return value from client.open_and_wait
(e.g., main_uri = await client.open_and_wait(workspace / "main.cpp")), which
triggers RUF059; fix by removing the binding and either await the call without
assignment or name it _main_uri (use the same approach for the other occurrences
of client.open_and_wait in this file), updating the calls where main_uri is
unused so no unused variable is created.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e12920da-6d60-40a4-87b5-3a877e8447c5

📥 Commits

Reviewing files that changed from the base of the PR and between de034b3 and a7d9548.

📒 Files selected for processing (6)
  • src/server/master_server.cpp
  • tests/conftest.py
  • tests/data/header_context/inner.h
  • tests/data/header_context/utils.h
  • tests/data/multi_context/main.cpp
  • tests/integration/test_header_context.py
✅ Files skipped from review due to trivial changes (2)
  • tests/data/header_context/inner.h
  • tests/data/header_context/utils.h

Comment thread src/server/master_server.cpp Outdated
Comment thread src/server/master_server.cpp Outdated
Comment thread tests/integration/test_header_context.py
- Replace host source file in args by matching against CDB file entry instead
  of removing last positional arg. Fixes "clang++ -c main.cpp -o main.o" case
  where the old logic would strip -o's argument and leave main.cpp in argv.
- Remove unused main_uri bindings in 3 tests (Ruff RUF059)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (7)
src/server/master_server.cpp (7)

2233-2247: ⚠️ Potential issue | 🟠 Major

Host-only invalidation leaves header preamble contexts stale.

At Line 2237, invalidation checks only hdr_ctx.host_path_id; saves to intermediate include-chain contributors can keep stale preambles alive. Also, include-graph-derived host resolution can go stale without reverse-map refresh after include-affecting saves.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 2233 - 2247, The current loop only
invalidates header contexts whose hdr_ctx.host_path_id equals the saved path_id,
leaving preamble contexts contributed via includes or include-graph mapping
stale; update the invalidation to also find headers that list the saved file as
a contributor or whose resolved host via the include graph now maps to the saved
path_id (use your include_graph / reverse include map helper), then erase those
hdr_id entries from header_file_contexts, mark their corresponding
documents[hdr_id].ast_dirty = true, and refresh or rebuild the include-graph
reverse map so subsequent saves see correct host resolution (apply changes
around header_file_contexts iteration and anywhere include-graph resolution is
cached).

707-718: ⚠️ Potential issue | 🟠 Major

active_contexts is still bypassed for files that already have direct CDB entries.

The early return at Line 707-Line 717 makes clice/switchContext ineffective on those files because override resolution never runs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 707 - 718, The early return after
using cdb.lookup bypasses active_contexts/override resolution and prevents
clice/switchContext from taking effect; instead of returning immediately in the
block that handles cdb.has_entry and results (the code that fills directory and
arguments from ctx and uses cdb.lookup/.has_entry), populate directory and
arguments from ctx but do not return—allow the function to continue so override
resolution (active_contexts or the existing override-resolution logic) can run
and potentially alter the chosen context; ensure any merge logic preserves
arguments populated from ctx and that subsequent override checks reference the
same variables (results/ctx/directory/arguments) so overrides can override or
replace the CDB-provided values.

2755-2816: ⚠️ Potential issue | 🔴 Critical

Negative offset can cause out-of-bounds pagination access.

At Line 2755 and Line 2815, a client-supplied negative offset leads to invalid indexing into all_items.

Suggested fix
-            int offset_val = params.offset.value_or(0);
+            int offset_val = std::max(0, params.offset.value_or(0));
             constexpr int page_size = 10;
@@
-            int end = std::min(offset_val + page_size, static_cast<int>(all_items.size()));
-            for(int i = offset_val; i < end; ++i) {
+            int start = std::min(offset_val, static_cast<int>(all_items.size()));
+            int end = std::min(start + page_size, static_cast<int>(all_items.size()));
+            for(int i = start; i < end; ++i) {
                 result.contexts.push_back(std::move(all_items[i]));
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 2755 - 2816, The code reads a
client-supplied offset into offset_val and then uses it to index into all_items
causing out-of-bounds when offset is negative; update the logic that sets/uses
offset_val (the variable offset_val and the pagination block that computes end
and loops over i) to sanitize the offset: clamp negative offsets to 0 (e.g.
offset_val = std::max(0, params.offset.value_or(0))) and also ensure offset_val
is no larger than all_items.size() (or return an empty result when offset_val >=
total) before computing end/page bounds so the for-loop over all_items[i] cannot
index out-of-range.

641-654: ⚠️ Potential issue | 🟠 Major

contains(next_filename) can match the wrong include edge.

At Line 653, basename substring matching is ambiguous and can pick the wrong line, which changes generated preamble content.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 641 - 654, The include detection
is using line.contains(next_filename) which can match substrings and pick the
wrong include; instead parse the include token from the directive (handle both
"#include" and "# include" forms), extract the quoted or angle-bracket filename
(strip surrounding '"' or '<' '>'), then compare that extracted filename exactly
to next_filename (or compare basenames explicitly if next_filename is a
basename) before setting include_line_start; update the logic around
content_ref, line_start and the line.starts_with checks to perform this exact
filename comparison.

594-607: ⚠️ Potential issue | 🟠 Major

Host/context selection is still non-deterministic and can pair the chain with the wrong compile command.

At Line 594-Line 607, selection stops at the first host with a CDB hit; at Line 748-Line 756, you re-lookup and take front() again. This can mismatch when a source has multiple CDB entries and does not enforce the advertised host preference strategy.

Also applies to: 748-756

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 594 - 607, The current
host/compile-command selection is non-deterministic because you pick the first
host with a CDB hit (using path_pool.resolve, cdb.lookup and
dependency_graph.find_include_chain) and later re-query and take cdb.front(),
which can return a different compile entry; instead, when you discover a valid
chain for a candidate host (in the loop that sets host_path_id and chain), also
capture and persist the exact CDB entry identifier or index returned by
cdb.lookup (e.g., the specific entry object or its unique key) and use that
stored entry later rather than calling cdb.lookup(...).front(); ensure both the
initial selection code and the later code (the block that currently rereads
front()) consult the same stored CDB entry for that host so the include chain
and compile command remain paired deterministically.

2856-2861: ⚠️ Potential issue | 🟠 Major

switchContext should validate that the selected context actually reaches the target header.

Right now success is granted on CDB presence alone; unrelated files can be accepted, then silently ignored later by fallback resolution.

Suggested fix
             auto context_cdb = cdb.lookup(context_path, {.suppress_logging = true});
             if(context_cdb.empty()) {
                 result.success = false;
                 co_return to_raw(result);
             }
+
+            // Ensure the selected context can actually compile the target file.
+            auto chain = dependency_graph.find_include_chain(context_path_id, path_id);
+            if(chain.empty()) {
+                result.success = false;
+                co_return to_raw(result);
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 2856 - 2861, The code in
switchContext only checks cdb.lookup(context_path) exists but doesn't verify
that this context can actually reach the requested target header; update
switchContext to validate reachability by confirming the returned context_cdb
includes/points to the target header (e.g. check that context_cdb contains an
entry or mapping for the target header identifier or that
cdb.resolve(context_path, target_header) succeeds) before setting
result.success; use the existing symbols context_path, context_cdb, and the
target header variable/name and fail (result.success = false; co_return) if the
context does not reach the target.

759-773: ⚠️ Potential issue | 🔴 Critical

Dropping the “last positional arg” can remove the wrong token and leave broken argv.

At Line 759-Line 767, removing by position can strip output paths (e.g., main.o) instead of the actual host source, leaving invalid flags and duplicate inputs.

Suggested fix
-    // Copy host arguments, replacing the source file path (last non-flag arg)
-    // with the header file path, so the compiler processes the header in context.
-    auto num_args = host_ctx.arguments.size();
-    std::size_t copy_count = num_args;
-    if(copy_count > 0) {
-        llvm::StringRef last(host_ctx.arguments[copy_count - 1]);
-        if(!last.starts_with("-"))
-            copy_count -= 1;
-    }
-    for(std::size_t i = 0; i < copy_count; ++i) {
-        arguments.emplace_back(host_ctx.arguments[i]);
-    }
-    // Append the header file path as the source file.
-    arguments.emplace_back(path);
+    // Copy host arguments and remove only source-file inputs, not arbitrary
+    // trailing positionals (e.g. -o <file> values).
+    auto is_source_input = [](llvm::StringRef tok) {
+        if(tok.empty() || tok.starts_with("-")) return false;
+        auto ext = llvm::sys::path::extension(tok);
+        return ext == ".c" || ext == ".cc" || ext == ".cpp" || ext == ".cxx" ||
+               ext == ".c++" || ext == ".C" || ext == ".m" || ext == ".mm";
+    };
+
+    for(auto* arg : host_ctx.arguments) {
+        llvm::StringRef tok(arg);
+        if(is_source_input(tok)) {
+            continue;
+        }
+        arguments.emplace_back(tok);
+    }
+    arguments.emplace_back(path);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 759 - 773, The current logic in
master_server.cpp that computes copy_count by simply removing the last non-flag
token (using host_ctx.arguments, copy_count, last) can drop the wrong positional
(e.g., output file). Replace this with a backward scan to locate the actual
source positional token: iterate host_ctx.arguments from the end to find the
last argument that does not start with "-" and is not the value of a flag that
expects a parameter (e.g., the token after "-o" or other known flags), treat
that index as the source to be excluded, set copy_count to that index, copy
host_ctx.arguments[0..copy_count-1] into arguments, then append path; use the
existing variables host_ctx.arguments, copy_count, arguments, and path to
implement this fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/server/master_server.cpp`:
- Around line 2233-2247: The current loop only invalidates header contexts whose
hdr_ctx.host_path_id equals the saved path_id, leaving preamble contexts
contributed via includes or include-graph mapping stale; update the invalidation
to also find headers that list the saved file as a contributor or whose resolved
host via the include graph now maps to the saved path_id (use your include_graph
/ reverse include map helper), then erase those hdr_id entries from
header_file_contexts, mark their corresponding documents[hdr_id].ast_dirty =
true, and refresh or rebuild the include-graph reverse map so subsequent saves
see correct host resolution (apply changes around header_file_contexts iteration
and anywhere include-graph resolution is cached).
- Around line 707-718: The early return after using cdb.lookup bypasses
active_contexts/override resolution and prevents clice/switchContext from taking
effect; instead of returning immediately in the block that handles cdb.has_entry
and results (the code that fills directory and arguments from ctx and uses
cdb.lookup/.has_entry), populate directory and arguments from ctx but do not
return—allow the function to continue so override resolution (active_contexts or
the existing override-resolution logic) can run and potentially alter the chosen
context; ensure any merge logic preserves arguments populated from ctx and that
subsequent override checks reference the same variables
(results/ctx/directory/arguments) so overrides can override or replace the
CDB-provided values.
- Around line 2755-2816: The code reads a client-supplied offset into offset_val
and then uses it to index into all_items causing out-of-bounds when offset is
negative; update the logic that sets/uses offset_val (the variable offset_val
and the pagination block that computes end and loops over i) to sanitize the
offset: clamp negative offsets to 0 (e.g. offset_val = std::max(0,
params.offset.value_or(0))) and also ensure offset_val is no larger than
all_items.size() (or return an empty result when offset_val >= total) before
computing end/page bounds so the for-loop over all_items[i] cannot index
out-of-range.
- Around line 641-654: The include detection is using
line.contains(next_filename) which can match substrings and pick the wrong
include; instead parse the include token from the directive (handle both
"#include" and "# include" forms), extract the quoted or angle-bracket filename
(strip surrounding '"' or '<' '>'), then compare that extracted filename exactly
to next_filename (or compare basenames explicitly if next_filename is a
basename) before setting include_line_start; update the logic around
content_ref, line_start and the line.starts_with checks to perform this exact
filename comparison.
- Around line 594-607: The current host/compile-command selection is
non-deterministic because you pick the first host with a CDB hit (using
path_pool.resolve, cdb.lookup and dependency_graph.find_include_chain) and later
re-query and take cdb.front(), which can return a different compile entry;
instead, when you discover a valid chain for a candidate host (in the loop that
sets host_path_id and chain), also capture and persist the exact CDB entry
identifier or index returned by cdb.lookup (e.g., the specific entry object or
its unique key) and use that stored entry later rather than calling
cdb.lookup(...).front(); ensure both the initial selection code and the later
code (the block that currently rereads front()) consult the same stored CDB
entry for that host so the include chain and compile command remain paired
deterministically.
- Around line 2856-2861: The code in switchContext only checks
cdb.lookup(context_path) exists but doesn't verify that this context can
actually reach the requested target header; update switchContext to validate
reachability by confirming the returned context_cdb includes/points to the
target header (e.g. check that context_cdb contains an entry or mapping for the
target header identifier or that cdb.resolve(context_path, target_header)
succeeds) before setting result.success; use the existing symbols context_path,
context_cdb, and the target header variable/name and fail (result.success =
false; co_return) if the context does not reach the target.
- Around line 759-773: The current logic in master_server.cpp that computes
copy_count by simply removing the last non-flag token (using host_ctx.arguments,
copy_count, last) can drop the wrong positional (e.g., output file). Replace
this with a backward scan to locate the actual source positional token: iterate
host_ctx.arguments from the end to find the last argument that does not start
with "-" and is not the value of a flag that expects a parameter (e.g., the
token after "-o" or other known flags), treat that index as the source to be
excluded, set copy_count to that index, copy host_ctx.arguments[0..copy_count-1]
into arguments, then append path; use the existing variables host_ctx.arguments,
copy_count, arguments, and path to implement this fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 00512512-efab-4686-8d28-a3a619087f90

📥 Commits

Reviewing files that changed from the base of the PR and between a7d9548 and 79c9aae.

📒 Files selected for processing (1)
  • src/server/master_server.cpp

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/server/master_server.cpp (1)

763-769: Path comparison may fail due to normalization differences.

The comparison llvm::StringRef(arg) == host_path assumes both paths use the same format. If the CDB entry stores a relative path while host_path is absolute (or uses different path separators/normalization), the match will fail and the header path won't be substituted.

Consider normalizing both paths before comparison, or matching against the CDB entry's file field directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/server/master_server.cpp` around lines 763 - 769, The loop that
substitutes header paths compares llvm::StringRef(arg) to host_path which can
fail due to differing path formats; update the comparison to normalize both
sides (e.g., convert arg and host_path to std::filesystem::path and compare
their canonical/weakly_canonical or lexically_normal forms) before deciding to
push path, or alternatively compare arg against the CDB entry's file field
directly (use host_ctx.file or the CDB file identifier) to ensure correct
matching; keep the rest of the logic (arguments.emplace_back(path) vs
arguments.emplace_back(arg)) unchanged.
tests/integration/test_header_context.py (1)

167-186: Hardcoded hover position is fragile.

Line 173 uses a fixed position (line=14, character=12) for the sum function. If utils.h is modified (even formatting changes), this test will break silently or hover on the wrong symbol. Consider adding a comment referencing the expected line content, or use a marker-based approach if the test framework supports it.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/integration/test_header_context.py` around lines 167 - 186, The test
uses a hardcoded Position(line=14, character=12) when calling
client.text_document_hover_async with HoverParams for utils_uri to hover the
'sum' symbol, which is fragile; change the test to locate the 'sum' token
dynamically (e.g., search the utils.h text for the 'sum' substring or use a
marker in the file) and compute the Position from that index before constructing
HoverParams, or add a stable marker comment in utils.h and find its position;
update the call sites around client.text_document_hover_async / HoverParams to
use the computed Position so the hover assertion remains resilient to formatting
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/server/master_server.cpp`:
- Around line 763-769: The loop that substitutes header paths compares
llvm::StringRef(arg) to host_path which can fail due to differing path formats;
update the comparison to normalize both sides (e.g., convert arg and host_path
to std::filesystem::path and compare their canonical/weakly_canonical or
lexically_normal forms) before deciding to push path, or alternatively compare
arg against the CDB entry's file field directly (use host_ctx.file or the CDB
file identifier) to ensure correct matching; keep the rest of the logic
(arguments.emplace_back(path) vs arguments.emplace_back(arg)) unchanged.

In `@tests/integration/test_header_context.py`:
- Around line 167-186: The test uses a hardcoded Position(line=14, character=12)
when calling client.text_document_hover_async with HoverParams for utils_uri to
hover the 'sum' symbol, which is fragile; change the test to locate the 'sum'
token dynamically (e.g., search the utils.h text for the 'sum' substring or use
a marker in the file) and compute the Position from that index before
constructing HoverParams, or add a stable marker comment in utils.h and find its
position; update the call sites around client.text_document_hover_async /
HoverParams to use the computed Position so the hover assertion remains
resilient to formatting changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 466b5407-1740-4854-9628-22d8ce471e7a

📥 Commits

Reviewing files that changed from the base of the PR and between 79c9aae and a7cee28.

📒 Files selected for processing (2)
  • src/server/master_server.cpp
  • tests/integration/test_header_context.py

16bit-ykiko and others added 6 commits April 6, 2026 16:54
…urce arg replacement

- Add pch_test compile_commands.json generation in conftest.py (was missing,
  causing all test_pch tests to hang on CI due to hardcoded local paths)
- Remove cdb.has_entry() check in fill_compile_args, use cdb.lookup() directly
- Replace host source arg by identity match instead of removing last positional arg
- Remove unused preamble_hash field from HeaderFileContext
- Remove -I from label generation (too noisy)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When switchContext has been called for a header file, fill_compile_args
must skip the normal cdb.lookup (which may return a toolchain fallback
entry without proper includes) and go directly to header context
resolution with preamble injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- fill_compile_args: checks active_contexts first (switchContext),
  then normal CDB lookup, then auto header context as fallback
- fill_header_context_args: uses host source's CDB entry with file
  path replaced and preamble injected
- Fixes: switchContext now correctly uses host's compile args instead
  of falling through to toolchain fallback

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace #include <vector> with custom types.h (Point struct) to avoid
slow PCH builds on Windows Debug CI. The test still validates non
self-contained header compilation via preamble injection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Parse #include directive to extract actual filename instead of substring
  match (fixes false positive when util.h matches util_base.h)
- Warn and fallback when host source path not found in CDB arguments
- Clamp negative queryContext offset to 0
- Add test: deep nested switchContext + hover (main.cpp -> utils.h -> inner.h)
- Add path_to_uri helper to CliceClient

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@16bit-ykiko 16bit-ykiko changed the title feat: header context support with queryContext/switchContext protocol feat: header context protocol — queryContext, currentContext, switchContext Apr 6, 2026
@16bit-ykiko 16bit-ykiko merged commit 836f415 into main Apr 6, 2026
15 checks passed
@16bit-ykiko 16bit-ykiko deleted the feat/header-context branch April 6, 2026 11:55
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