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

Function matchGoFieldChainCall

src/resolution/name-matcher.ts:1919–1981  ·  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

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