feat(dynamic-calls): Phase 6 Rust parity — add GetMethod/Invoke/apply detection to Rust extractors (C#, Swift, Elixir, Lua)#1670
Conversation
… detection to Rust extractors C# (csharp.rs): GetMethod/GetRuntimeMethod/GetDeclaredMethod → reflection (literal) or computed-key; Invoke → unresolved-dynamic. Mirrors TS handleCsInvocationExpr. Swift (swift.rs): NSSelectorFromString(literal) → reflection; NSSelectorFromString(expr) → unresolved-dynamic; performSelector → unresolved-dynamic. Mirrors TS handleSwiftCallExpression. Elixir (elixir.rs): apply(mod, :atom, args) → reflection; apply(mod, var, args) → computed-key. Mirrors TS handleElixirCall apply case. Lua (lua.rs): load/loadstring/dofile → eval; t[k]() bracket_index_expression → computed-key (or resolves to literal if string key). Mirrors TS handleLuaFunctionCall. Closes #1656
Codegraph Impact Analysis25 functions changed → 31 callers affected across 15 files
|
| const resultJson = ctx.nativeDb.buildGraph( | ||
| ctx.rootDir, | ||
| JSON.stringify(ctx.config), | ||
| JSON.stringify(ctx.aliases), | ||
| JSON.stringify(ctx.opts), | ||
| ); | ||
| // Restore FK enforcement for JS post-passes (CHA, dataflow, structure). | ||
| try { | ||
| ctx.nativeDb.exec('PRAGMA foreign_keys = ON'); | ||
| } catch { | ||
| // exec may not exist on very old addon versions — safe to ignore | ||
| } |
There was a problem hiding this comment.
FK enforcement left OFF on
buildGraph throw
If buildGraph() throws (e.g. OOM, parse error, or binary crash), the PRAGMA foreign_keys = ON restoration is never reached. Every JS post-pass (CHA, dataflow, structure) that runs on the same ctx.nativeDb connection afterward would then execute without FK enforcement, potentially allowing inconsistent rows that should have been rejected. The fix is to wrap buildGraph() in a try/finally so the PRAGMA foreign_keys = ON executes unconditionally.
| const resultJson = ctx.nativeDb.buildGraph( | |
| ctx.rootDir, | |
| JSON.stringify(ctx.config), | |
| JSON.stringify(ctx.aliases), | |
| JSON.stringify(ctx.opts), | |
| ); | |
| // Restore FK enforcement for JS post-passes (CHA, dataflow, structure). | |
| try { | |
| ctx.nativeDb.exec('PRAGMA foreign_keys = ON'); | |
| } catch { | |
| // exec may not exist on very old addon versions — safe to ignore | |
| } | |
| let resultJson: string; | |
| try { | |
| resultJson = ctx.nativeDb.buildGraph( | |
| ctx.rootDir, | |
| JSON.stringify(ctx.config), | |
| JSON.stringify(ctx.aliases), | |
| JSON.stringify(ctx.opts), | |
| ); | |
| } finally { | |
| // Restore FK enforcement for JS post-passes (CHA, dataflow, structure). | |
| try { | |
| ctx.nativeDb.exec('PRAGMA foreign_keys = ON'); | |
| } catch { | |
| // exec may not exist on very old addon versions — safe to ignore | |
| } | |
| } |
| if method_name == "Invoke" { | ||
| symbols.calls.push(Call { | ||
| name: node_text(&name, source).to_string(), | ||
| line: start_line(node), | ||
| dynamic: None, | ||
| name: "<dynamic:unresolved>".to_string(), | ||
| line: call_line, | ||
| dynamic: Some(true), | ||
| dynamic_kind: Some("unresolved-dynamic".to_string()), | ||
| receiver, | ||
| ..Default::default() | ||
| }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
All
.Invoke() calls classified as unresolved-dynamic
The current check catches every receiver.Invoke(...) call, not just MethodInfo.Invoke. Standard delegate and lambda invocations (myFunc.Invoke(arg), Action.Invoke(), LINQ delegate chains) are all statically knowable but will now be flagged as unresolved-dynamic and emit sink edges. This is parity with the TS extractor, but worth verifying that C# code heavy on delegate .Invoke() patterns doesn't accumulate noisy sink edges that inflate unresolved-dynamic counts.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
4554cc4 to
f9e4e20
Compare
Summary
GetMethod/GetRuntimeMethod/GetDeclaredMethod→reflectionandInvoke→unresolved-dynamicdetection tocsharp.rs, matching the TShandleCsInvocationExprimplementationNSSelectorFromString(literal →reflection, computed →unresolved-dynamic) andperformSelector→unresolved-dynamictoswift.rs, matching the TShandleSwiftCallExpressionimplementationapply(mod, :atom, args)→reflectionandapply(mod, var, args)→computed-keytoelixir.rs, matching the TSapplycase inhandleElixirCallload/loadstring/dofile→evalandt[k]()(bracket_index_expression) →computed-keytolua.rs, matching the TShandleLuaFunctionCallimplementationTest plan
cargo test -p codegraph-core --lib)tests/parsers/csharp.test.ts,swift.test.ts,elixir.test.ts,lua.test.ts)tests/engines/parity.test.ts)Closes #1656