MCPcopy Create free account
hub / github.com/colbymchenry/codegraph / matchGoFieldChainCall

Function matchGoFieldChainCall

src/resolution/name-matcher.ts:1931–1993  ·  view source on GitHub ↗

* Resolve a Go 2-hop field-chain call `base.field.Method(...)` (#1276): * `target.conn.Exec("insert")` where `func (target *Target) Write()` and * `type Target struct { conn *sql.DB }`. Two inference hops, both read from * source the same way #1108 does: * 1. `base`'s type from the enclosing s

(
  receiverChain: string,
  methodName: string,
  ref: UnresolvedRef,
  context: ResolutionContext
)

Source from the content-addressed store, hash-verified

1929 * receivers, so the ref stays unresolved instead of name-guessing.
1930 */
1931function matchGoFieldChainCall(
1932 receiverChain: string,
1933 methodName: string,
1934 ref: UnresolvedRef,
1935 context: ResolutionContext
1936): ResolvedRef | null {
1937 const segs = receiverChain.split('.');
1938 if (segs.length !== 2 || !segs[0] || !segs[1]) return null;
1939 const [base, field] = segs;
1940
1941 const baseType = inferLocalReceiverType(base!, ref, context);
1942 if (!baseType) return null;
1943
1944 const fieldEsc = field!.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1945 const fieldTypeRe = new RegExp(`\\b${fieldEsc}\\s+\\*?\\[?\\]?([A-Za-z_][\\w.]*)`);
1946
1947 const structs = preferCallSiteFile(context.getNodesByName(baseType), ref.filePath).filter(
1948 (n) => (n.kind === 'struct' || n.kind === 'class') && n.language === 'go'
1949 );
1950 for (const s of structs) {
1951 const source = context.readFile(s.filePath);
1952 if (!source) continue;
1953 // Only the struct's own declaration lines — a same-named identifier
1954 // elsewhere in the file can't donate a type. Matched LINE BY LINE with
1955 // comments stripped: chi's `Mux` has a doc comment reading "the tree
1956 // router" right above `tree *node`, and a whole-block match captured
1957 // `router` from the prose instead of `node` from the field.
1958 const declLines = source.split('\n').slice(Math.max(0, s.startLine - 1), s.endLine);
1959 for (const rawLine of declLines) {
1960 const line = rawLine.replace(/\/\/.*$/, '').replace(/\/\*.*?\*\//g, '');
1961 const m = line.match(fieldTypeRe);
1962 if (!m || !m[1]) continue;
1963 const rawType = m[1];
1964 // A package-qualified field type (`http.Handler`, `sql.DB`) is only
1965 // followed when the package is IN-MODULE: stripping the qualifier and
1966 // matching the bare name would conflate a stdlib/third-party type with
1967 // any same-named project type — on chi, `handler http.Handler` bound
1968 // to an example app's unrelated local `Handler`. That is the exact
1969 // fabrication this matcher exists to prevent (#1276).
1970 if (rawType.includes('.')) {
1971 const pkg = rawType.split('.')[0]!;
1972 const mod = context.getGoModule?.();
1973 const imp = context
1974 .getImportMappings(s.filePath, 'go')
1975 .find((i) => i.localName === pkg);
1976 const inModule =
1977 !!mod &&
1978 !!imp &&
1979 (imp.source === mod.modulePath || imp.source.startsWith(mod.modulePath + '/'));
1980 if (!inModule) continue;
1981 }
1982 // Unexported (lowercase) types are idiomatic Go and stay eligible —
1983 // chi's `mx.tree.FindRoute()` chains through `tree *node`. A
1984 // mis-capture is harmless: resolveMethodOnType only returns a
1985 // validated `<type>::<method>` match.
1986 const fieldType = rawType.split('.').pop();
1987 if (!fieldType || !/^[A-Za-z_]/.test(fieldType) || GO_BUILTIN_FIELD_TYPES.has(fieldType)) continue;
1988 const resolved = resolveMethodOnType(fieldType, methodName, ref, context, 0.85, 'instance-method');

Callers 1

matchMethodCallFunction · 0.85

Calls 8

inferLocalReceiverTypeFunction · 0.85
preferCallSiteFileFunction · 0.85
resolveMethodOnTypeFunction · 0.85
readFileMethod · 0.80
getGoModuleMethod · 0.80
getImportMappingsMethod · 0.80
hasMethod · 0.80
getNodesByNameMethod · 0.65

Tested by

no test coverage detected