Skip to content

Fix: emit extern "C" fn for functions declared in extern "C" blocks#4210

Merged
riverar merged 7 commits intomasterfrom
copilot/fix-extern-c-functions
Apr 18, 2026
Merged

Fix: emit extern "C" fn for functions declared in extern "C" blocks#4210
riverar merged 7 commits intomasterfrom
copilot/fix-extern-c-functions

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 18, 2026

Functions declared inside extern "C" { } blocks in C++ headers were being entirely ignored — not collected at all — because process_tu in crates/libs/rdl/src/clang/mod.rs did not handle CXCursor_LinkageSpec nodes. Additionally, when such functions are collected, they must be emitted with extern "C" fn (cdecl) rather than extern fn (system/stdcall ABI) to correctly represent their calling convention.

All changes are scoped to crates/libs/rdl.

Changes

  • crates/libs/rdl/src/clang/cx.rs

    • Add Cursor::language() wrapping clang_getCursorLanguage from clang-sys
  • crates/libs/rdl/src/clang/fn.rs

    • Add extern_c: bool to Fn to track C-linkage origin
    • Update parse() to accept the extern_c flag
    • Update write() to emit extern "C" fn (via conditional quote!) when extern_c is set
  • crates/libs/rdl/src/clang/mod.rs

    • Pass extern_c = false for top-level CXCursor_FunctionDecl items
    • Add CXCursor_LinkageSpec arm that recurses into the block's children and collects any contained FunctionDecl with extern_c = (language == CXLanguage_C)
  • New test: crates/tests/libs/clang/roundtrip/fn_extern_c.h / .rdl — round-trip coverage for extern "C" { } blocks

Example

Input header (C++ mode):

extern "C" {
    unsigned int GetTickCount();
    void SetLastErrorEx(unsigned a, signed b);
}

Before:

// (empty — functions were ignored entirely)

After:

extern "C" fn GetTickCount() -> u32;
extern "C" fn SetLastErrorEx(a: u32, b: i32);

Functions declared inside `extern "C" { }` blocks in C++ headers were
being collected but emitted with `extern fn` (defaulting to the
`system`/PlatformApi calling convention) instead of the correct
`extern "C" fn` (C/cdecl calling convention).

Fix:
- Add `extern_c: bool` field to `RdlFn`
- Detect C language linkage via `Entity::get_language() == Some(Language::C)`
  when in C++ mode
- Pass the flag through `collect_function()` and emit `extern \"C\" fn`
  in `emit_fn()` when the flag is set

Add a new test case (`fn_extern_c.h` + golden `.rdl`) that exercises
`extern \"C\" { }` blocks.

Update `webview2.rdl` golden file since WebView2 functions are also
declared inside `extern \"C\"` blocks and now correctly emit `extern \"C\" fn`.

Agent-Logs-Url: https://github.com/microsoft/windows-rs/sessions/ec069672-f3b6-4b00-8f4c-bcc4140be864

Co-authored-by: riverar <475132+riverar@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix extern "C" functions handling in RDL generation Fix: emit extern "C" fn for functions declared in extern "C" blocks Apr 18, 2026
Copilot AI requested a review from riverar April 18, 2026 01:33
Copy link
Copy Markdown
Collaborator

@riverar riverar left a comment

Choose a reason for hiding this comment

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

Do not change the header2rdl crate. Focus on crates/libs/rdl instead. Start over.

…ates/libs/rdl)

Functions declared inside `extern "C" { }` blocks were being ignored
entirely because `process_tu` in `crates/libs/rdl/src/clang/mod.rs`
did not handle `CXCursor_LinkageSpec` nodes.

Fix in `crates/libs/rdl/src/clang/`:
- `cx.rs`: Add `Cursor::language()` wrapping `clang_getCursorLanguage`
- `fn.rs`: Add `extern_c: bool` to `Fn`; pass flag through `parse()`;
  emit `extern \"C\" fn` in `write()` when set (using conditional quote)
- `mod.rs`: Pass `extern_c = false` for top-level `FunctionDecl`;
  add `CXCursor_LinkageSpec` arm that recurses into children and
  sets `extern_c = (language == CXLanguage_C)` for each `FunctionDecl`

Also reverts the previous (incorrect) changes to `crates/tools/header2rdl/`.

New test: `crates/tests/libs/clang/roundtrip/fn_extern_c.h` / `.rdl`

Agent-Logs-Url: https://github.com/microsoft/windows-rs/sessions/57f06e7c-6509-4233-97cc-bd5ad23a4b08

Co-authored-by: riverar <475132+riverar@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 18, 2026

Do not change the header2rdl crate. Focus on crates/libs/rdl instead. Start over.

Done. Rewrote the fix in crates/libs/rdl/src/clang/ (commit 8cb2462):

  • cx.rs: Added Cursor::language() wrapping clang_getCursorLanguage
  • fn.rs: Added extern_c: bool to Fn; write() now emits extern "C" fn when set
  • mod.rs: Added CXCursor_LinkageSpec handling that recurses into the block's children, passing extern_c = (language == CXLanguage_C) for each contained FunctionDecl

Reverted all header2rdl crate changes.

Copilot AI requested a review from riverar April 18, 2026 04:48
Comment thread crates/tests/libs/clang/roundtrip/enum.rdl Outdated
Copilot AI requested a review from riverar April 18, 2026 05:00
@riverar riverar requested a review from kennykerr April 18, 2026 05:36
@riverar riverar marked this pull request as ready for review April 18, 2026 05:36
@riverar
Copy link
Copy Markdown
Collaborator

riverar commented Apr 18, 2026

@copilot Address the build failure here.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 18, 2026

@copilot Address the build failure here.

Fixed in commit 23c710a — ran cargo fmt on fn.rs and mod.rs to reformat the long function signatures.

Copilot AI requested a review from riverar April 18, 2026 05:48
Comment thread crates/tests/libs/clang/roundtrip/enum.rdl
Co-authored-by: riverar <475132+riverar@users.noreply.github.com>
Copilot AI requested a review from riverar April 18, 2026 06:37
@riverar riverar merged commit dd846ee into master Apr 18, 2026
26 checks passed
@riverar riverar deleted the copilot/fix-extern-c-functions branch April 18, 2026 16:55
Copilot AI added a commit that referenced this pull request Apr 20, 2026
…4210)

Co-authored-by: kennykerr <9845234+kennykerr@users.noreply.github.com>
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.

windows-rdl / clang: functions in extern "C" blocks get ignored

3 participants