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

Function resolve

src/resolution/frameworks/java.ts:60–195  ·  view source on GitHub ↗
(ref: UnresolvedRef, context: ResolutionContext)

Source from the content-addressed store, hash-verified

58 },
59
60 resolve(ref: UnresolvedRef, context: ResolutionContext): ResolvedRef | null {
61 // Spring config-key references — `@Value("${key}")` (single leaf) and
62 // `@ConfigurationProperties(prefix="X")` (entire subtree, marked with the
63 // `:prefix` suffix in extractSpringValueBindings). Lookup goes through
64 // Spring's relaxed binding (kebab/camel/snake → canonical lowercase).
65 if (ref.referenceName.endsWith(':prefix')) {
66 const prefix = ref.referenceName.slice(0, -':prefix'.length);
67 const canonPrefix = canonicalConfigKey(prefix);
68 // Prefer an exact prefix match (one node = the prefix subtree). Without
69 // node-level subtree representation we map to the closest matching key.
70 const candidates = context.getNodesByKind('constant').filter(
71 (n) => (n.language === 'yaml' || n.language === 'properties')
72 && canonicalConfigKey(n.qualifiedName).startsWith(canonPrefix),
73 );
74 if (candidates.length === 0) return null;
75 // Pick the SHORTEST canonical name — it's the closest binding point
76 // (`app.cache` over `app.cache.name.user-token` for prefix=`app.cache`).
77 const best = candidates.reduce((a, b) =>
78 canonicalConfigKey(a.qualifiedName).length <= canonicalConfigKey(b.qualifiedName).length ? a : b,
79 );
80 return { original: ref, targetNodeId: best.id, confidence: 0.85, resolvedBy: 'framework' };
81 }
82 if (ref.referenceName.includes('.') && ref.language !== 'java' && ref.language !== 'kotlin') {
83 // Spring config dotted key — only when the source language is Java/Kotlin
84 // (the bindings come from `@Value`). Skip non-Spring refs that happen to
85 // have dots in them.
86 }
87 // Spring config-key resolution: `@Value("${a.b.c}")` and
88 // `@ConfigurationProperties`. Gate on the `references` kind — those bindings
89 // are emitted as `references` by extractSpringValueBindings, whereas the far
90 // more numerous method-call refs (`list.add()`, `builder.build()`, every
91 // `receiver.method()`) are `calls`. A config key is NEVER a `calls` ref, so
92 // this loses no resolution. Without the gate, every dotted `calls` ref fell
93 // into the uncached getNodesByKind('constant') scan below — an
94 // O(dotted-calls × constant-nodes) cost that dominated resolution and made a
95 // full index take ~1h on large Java/Kotlin (Spring) monorepos (#1180). The
96 // old `split('.').length >= 2` heuristic couldn't separate keys from calls;
97 // the kind check does it exactly.
98 if (
99 ref.referenceKind === 'references' &&
100 (ref.language === 'java' || ref.language === 'kotlin') &&
101 ref.referenceName.includes('.') &&
102 !ref.referenceName.includes('::')
103 ) {
104 const canonRef = canonicalConfigKey(ref.referenceName);
105 const candidates = context.getNodesByKind('constant').filter(
106 (n) => n.kind === 'constant'
107 && (n.language === 'yaml' || n.language === 'properties')
108 && canonicalConfigKey(n.qualifiedName) === canonRef,
109 );
110 if (candidates.length === 1) {
111 return { original: ref, targetNodeId: candidates[0]!.id, confidence: 0.9, resolvedBy: 'framework' };
112 }
113 if (candidates.length > 1) {
114 // Multiple profile-specific files (application-dev.yml +
115 // application-prod.yml) can define the same key. Prefer the one with
116 // the shortest profile suffix (the base `application.yml` wins over
117 // profile variants when both exist), then by alphabetical path so the

Callers

nothing calls this directly

Calls 4

canonicalConfigKeyFunction · 0.85
scoreFunction · 0.85
resolveByNameAndKindFunction · 0.70
getNodesByKindMethod · 0.65

Tested by

no test coverage detected