Files
wehub-resource-sync a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:33:42 +08:00

255 lines
10 KiB
Go

package languages
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// cValueRefCandidates returns the function-as-value candidates the C extractor
// captured for src — the placeholder edges (via=callback_candidate) awaiting the
// resolver gate — as a name → source-line map. It is the capture-side view a
// generated command table exercises before any resolution runs.
func cValueRefCandidates(t *testing.T, path, src string) map[string]int {
t.Helper()
r, err := NewCExtractor().Extract(path, []byte(src))
require.NoError(t, err)
out := map[string]int{}
for _, e := range r.Edges {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v != "callback_candidate" {
continue
}
if name, _ := e.Meta["fn_value_name"].(string); name != "" {
out[name] = e.Line
}
}
return out
}
// TestCExtractor_CommandTableMacroCandidates pins the redis command-table shape:
// a `MAKE_CMD(..., handler, ...)` macro row in a generated `.def` fragment
// captures the handler as a function-value candidate, while the string summary,
// the ALL_CAPS flag macro, the short arity counts, and the MAKE_CMD callee
// itself do not become references.
func TestCExtractor_CommandTableMacroCandidates(t *testing.T) {
src := "" +
"struct redisCommand redisCommandTable[] = {\n" + // line 1
"{MAKE_CMD(\"get\", \"Get the value\", -2, CMD_READONLY, getCommand, 1, 2)},\n" + // line 2
"{MAKE_CMD(\"strlen\", \"String length\", 2, CMD_READONLY, strlenCommand, 1, 2)},\n" + // line 3
"};\n"
cands := cValueRefCandidates(t, "commands.def", src)
assert.Equal(t, 2, cands["getCommand"], "handler in a macro arg list is captured")
assert.Equal(t, 3, cands["strlenCommand"], "handler in a macro arg list is captured")
assert.NotContains(t, cands, "CMD_READONLY", "an ALL_CAPS flag macro is not a function reference")
assert.NotContains(t, cands, "MAKE_CMD", "the macro callee is a call, not a value reference")
assert.NotContains(t, cands, "get", "a short string-literal table key is not an identifier reference")
assert.NotContains(t, cands, "Get the value", "a string literal is never captured")
}
// TestCExtractor_DispatchTableInitListCandidates covers the classic positional
// initializer-list dispatch table `{ "name", fnPtr, arity }` — the handler in
// the second slot is captured as a function-value candidate.
func TestCExtractor_DispatchTableInitListCandidates(t *testing.T) {
src := "" +
"struct cmd table[] = {\n" + // line 1
"{ \"ping\", pingCommand, 2 },\n" + // line 2
"{ \"echo\", echoCommand, 2 },\n" + // line 3
"};\n"
cands := cValueRefCandidates(t, "table.c", src)
assert.Equal(t, 2, cands["pingCommand"], "handler in an initializer-list slot is captured")
assert.Equal(t, 3, cands["echoCommand"], "handler in an initializer-list slot is captured")
assert.NotContains(t, cands, "ping", "a string key is not an identifier reference")
}
// TestCExtractor_CommandTableNoiseNotCaptured is the negative precision pin: the
// three noise classes the guard exists for — ALL_CAPS macros / enum constants,
// sub-4-character identifiers, and string literals — never become candidates,
// while the one mixed-case, long-enough handler in the same row does.
func TestCExtractor_CommandTableNoiseNotCaptured(t *testing.T) {
src := "" +
"struct redisCommand t[] = {\n" + // line 1
"{MAKE_CMD(\"noise\", ARG_TYPE_KEY, CMD_WRITE, run, xy, realCommand)},\n" + // line 2
"};\n"
cands := cValueRefCandidates(t, "n.def", src)
assert.Contains(t, cands, "realCommand", "the mixed-case, long-enough handler is captured")
assert.NotContains(t, cands, "ARG_TYPE_KEY", "an ALL_CAPS enum constant is filtered")
assert.NotContains(t, cands, "CMD_WRITE", "an ALL_CAPS flag macro is filtered")
assert.NotContains(t, cands, "run", "a sub-4-character identifier is filtered")
assert.NotContains(t, cands, "xy", "a two-character identifier is filtered")
}
// TestCExtractor_CommandTableErrorRecovery pins that a malformed generated
// fragment (a missing close paren plus a line of garbage — the shape a `.def`
// degrades to when its surrounding translation-unit context is absent) does not
// suppress extraction: the extractor recovers the handlers whose argument lists
// still parse rather than emitting nothing.
func TestCExtractor_CommandTableErrorRecovery(t *testing.T) {
src := "" +
"MAKE_CMD(\"get\", getCommand, 2)\n" + // line 1: clean
"MAKE_CMD(\"strlen\", strlenCommand, 2\n" + // line 2: missing close paren
"%%% garbage !!! {{{ nonsense\n" + // line 3: junk
"MAKE_CMD(\"append\", appendCommand, 3)\n" // line 4
r, err := NewCExtractor().Extract("broken.def", []byte(src))
require.NoError(t, err, "extraction must not fail on an ERROR-recovered tree")
cands := map[string]bool{}
for _, e := range r.Edges {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v != "callback_candidate" {
continue
}
if name, _ := e.Meta["fn_value_name"].(string); name != "" {
cands[name] = true
}
}
assert.True(t, cands["getCommand"], "the cleanly-parsed row before the error survives")
assert.True(t, cands["strlenCommand"], "the handler still in an arg list after the missing paren survives recovery")
}
// TestCExtractor_LexerLookaheadFilteredAndDeduped pins the generated-lexer flood
// shape. An uninitialised local (`int32_t lookahead;`) compared on ==/!= across
// many lines is a local, never a function address, so it yields no candidate; a
// genuinely-undeclared free name compared the same way collapses to exactly one
// candidate regardless of how many lines mention it (the per-name dedup).
func TestCExtractor_LexerLookaheadFilteredAndDeduped(t *testing.T) {
src := "" +
"void lexScan(void) {\n" + // line 1
" int32_t lookahead;\n" + // line 2: uninitialised local
" if (lookahead == 65) return;\n" + // 3
" if (lookahead != 66) return;\n" + // 4
" if (lookahead == 67) return;\n" + // 5
" if (externScanHook == 68) return;\n" + // 6: undeclared free name
" if (externScanHook != 69) return;\n" + // 7
" if (externScanHook == 70) return;\n" + // 8
"}\n"
r, err := NewCExtractor().Extract("lexer.c", []byte(src))
require.NoError(t, err)
counts := map[string]int{}
for _, e := range r.Edges {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v != "callback_candidate" {
continue
}
if name, _ := e.Meta["fn_value_name"].(string); name != "" {
counts[name]++
}
}
assert.Zero(t, counts["lookahead"], "an uninitialised local compared on ==/!= is never a function-address candidate")
assert.Equal(t, 1, counts["externScanHook"], "an undeclared free name on N comparison lines collapses to one candidate")
}
// TestCExtractor_EnumMembersNotCaptured pins the generated-parser enum flood: an
// in-file anonymous enum's members, referenced from a designated-initializer
// action table, are compile-time constants — never function addresses — so none
// becomes a candidate, while a genuine cross-TU handler in the same table does.
func TestCExtractor_EnumMembersNotCaptured(t *testing.T) {
src := "" +
"enum { sym_alpha, sym_beta, sym_gamma };\n" + // line 1
"void *actionTable[] = {\n" + // line 2
" [0] = sym_alpha,\n" + // 3
" [1] = sym_beta,\n" + // 4
" [2] = sym_gamma,\n" + // 5
" [3] = reduceAction,\n" + // 6: a real cross-TU handler
"};\n"
cands := cValueRefCandidates(t, "parser.c", src)
assert.NotContains(t, cands, "sym_alpha", "an in-file enum member is not a function reference")
assert.NotContains(t, cands, "sym_beta", "an in-file enum member is not a function reference")
assert.NotContains(t, cands, "sym_gamma", "an in-file enum member is not a function reference")
assert.Contains(t, cands, "reduceAction", "a genuine handler in the same table is still captured")
}
// TestCExtractor_PerFileCandidateFuse pins the per-file fuse: a pathological
// generated file with more distinct value-position handlers than the cap emits
// exactly the cap — the bound that stops a generated dispatch table from
// exploding the placeholder set.
func TestCExtractor_PerFileCandidateFuse(t *testing.T) {
var b strings.Builder
b.WriteString("void *bigDispatch[] = {\n")
for i := 0; i < cFnAddressMaxPerFile+200; i++ {
fmt.Fprintf(&b, " handlerFn%05d,\n", i)
}
b.WriteString("};\n")
r, err := NewCExtractor().Extract("generated.c", []byte(b.String()))
require.NoError(t, err)
n := 0
for _, e := range r.Edges {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v == "callback_candidate" {
n++
}
}
assert.Equal(t, cFnAddressMaxPerFile, n, "the per-file fuse caps emitted candidates at the const bound")
}
// TestCExtractor_DispatchTableCandidateFields pins the candidate metadata a
// realistic cross-TU dispatch table produces: each handler is captured exactly
// once (dedup), marked ungated so the resolver binds it cross-translation-unit,
// tagged with the capturing grammar, and carries no address-of form (a plain
// value slot).
func TestCExtractor_DispatchTableCandidateFields(t *testing.T) {
src := "" +
"struct cmd table[] = {\n" + // line 1
"{ \"get\", getCommand, 2 },\n" + // 2
"{ \"set\", setCommand, 3 },\n" + // 3
"};\n"
r, err := NewCExtractor().Extract("dispatch.c", []byte(src))
require.NoError(t, err)
type capture struct {
count int
ungated bool
form string
lang string
}
got := map[string]capture{}
for _, e := range r.Edges {
if e.Meta == nil {
continue
}
if v, _ := e.Meta["via"].(string); v != "callback_candidate" {
continue
}
name, _ := e.Meta["fn_value_name"].(string)
if name == "" {
continue
}
c := got[name]
c.count++
if u, _ := e.Meta["fn_value_ungated"].(bool); u {
c.ungated = true
}
c.form, _ = e.Meta["fn_ref_form"].(string)
c.lang, _ = e.Meta["fn_ref_lang"].(string)
got[name] = c
}
for _, name := range []string{"getCommand", "setCommand"} {
c := got[name]
assert.Equal(t, 1, c.count, "%s is captured exactly once", name)
assert.True(t, c.ungated, "%s is ungated for cross-TU binding", name)
assert.Equal(t, "", c.form, "%s sits in a plain value position (no address-of form)", name)
assert.Equal(t, "c", c.lang, "%s is tagged with the capturing grammar", name)
}
}