A static function in C is visible only in its own translation unit, but the exact-name strategy accepts it as the target of a call from any other file. When the caller's file has no same-named definition and another file has a static one, the call resolves onto it.
Reproduction
core.c:
void coreRun(void)
{
usbGetDescriptor();
}
usb_audio.c:
static void usbGetDescriptor(void)
{
}
codegraph init, then the edges of coreRun:
coreRun --calls--> usb_audio.c::usbGetDescriptor resolvedBy: exact-match
The real target is whatever core.c links against — a non-static usbGetDescriptor elsewhere, a function pointer, a macro — but never that one. When a same-file static of the same name exists the proximity score in findBestMatch picks it, so the wrong edge appears exactly when the caller's file has no definition, which is the case that matters for cross-file navigation.
Scale
On a 2,109-file betaflight tree, of 42,460 cross-file calls/imports edges resolved by exact-match or fuzzy, 4,448 target a function declared static in a file other than the caller's (10%). A typical run of them:
lib/main/AT32F43x/middlewares/usb_drivers/src/usbd_sdr.c:usbd_get_descriptor
→ lib/main/AT32F43x/middlewares/usbd_class/audio/audio_desc.c:get_device_descriptor (static)
→ …/audio/audio_desc.c:get_device_configuration (static)
→ …/audio/audio_desc.c:get_device_lang_id (static)
usbd_sdr.c calls those through a descriptor table of function pointers; audio_desc.c, cdc_desc.c, hid_desc.c, … each define their own static get_device_descriptor, and the matcher hands every call to the first file it ranks.
This is the C form of the same defect: a binding that the language makes file-local is admitted as a cross-file candidate. #1720's isSealedModule is deliberately JS/TS-only. In C the signal is per symbol, not per file — the definition's storage class — and the extractor already sees the static specifier on the function_definition, so it could either mark the node (a visibility/isStatic flag) or the resolver could read the definition line as #1720 reads source. Same-file candidates must stay eligible, and a static in a header included by the caller is a genuine same-unit call, which is the one exception to keep in mind.
A
staticfunction in C is visible only in its own translation unit, but the exact-name strategy accepts it as the target of a call from any other file. When the caller's file has no same-named definition and another file has astaticone, the call resolves onto it.Reproduction
core.c:usb_audio.c:codegraph init, then the edges ofcoreRun:The real target is whatever
core.clinks against — a non-staticusbGetDescriptorelsewhere, a function pointer, a macro — but never that one. When a same-filestaticof the same name exists the proximity score infindBestMatchpicks it, so the wrong edge appears exactly when the caller's file has no definition, which is the case that matters for cross-file navigation.Scale
On a 2,109-file betaflight tree, of 42,460 cross-file
calls/importsedges resolved by exact-match or fuzzy, 4,448 target a function declaredstaticin a file other than the caller's (10%). A typical run of them:usbd_sdr.ccalls those through a descriptor table of function pointers;audio_desc.c,cdc_desc.c,hid_desc.c, … each define their ownstatic get_device_descriptor, and the matcher hands every call to the first file it ranks.Relationship to #1719 / #1720
This is the C form of the same defect: a binding that the language makes file-local is admitted as a cross-file candidate. #1720's
isSealedModuleis deliberately JS/TS-only. In C the signal is per symbol, not per file — the definition's storage class — and the extractor already sees thestaticspecifier on thefunction_definition, so it could either mark the node (avisibility/isStaticflag) or the resolver could read the definition line as #1720 reads source. Same-file candidates must stay eligible, and astaticin a header included by the caller is a genuine same-unit call, which is the one exception to keep in mind.