( ref: UnresolvedRef, context: ResolutionContext )
| 2227 | } |
| 2228 | |
| 2229 | export function matchReference( |
| 2230 | ref: UnresolvedRef, |
| 2231 | context: ResolutionContext |
| 2232 | ): ResolvedRef | null { |
| 2233 | // Function-as-value refs (#756) resolve ONLY through the dedicated matcher — |
| 2234 | // never the fuzzy/qualified fallthrough below (a wrong callback edge is |
| 2235 | // worse than none). |
| 2236 | if (ref.referenceKind === 'function_ref') { |
| 2237 | return matchFunctionRef(ref, context); |
| 2238 | } |
| 2239 | |
| 2240 | // ArkTS chained UI attributes — emitted with a leading dot (`.titleStyle`, |
| 2241 | // `.width`) by the extractor — resolve ONLY to decorator-marked attribute |
| 2242 | // helpers: `@Extend`/`@Styles`/`@AnimatableExtend` functions (and global |
| 2243 | // `@Builder`s used attribute-position). Framework attributes (`.width`, |
| 2244 | // `.fontSize` — on nearly every UI line) match no such helper and stay |
| 2245 | // unresolved, NEVER falling through to bare-name matching: on a samples |
| 2246 | // monorepo that fallthrough manufactured 36k wrong edges, giving single |
| 2247 | // same-named properties thousands of false callers. Ambiguity rule matches |
| 2248 | // the rest of the file: several same-named helpers → prefer the call-site |
| 2249 | // file, still ambiguous → drop the ref rather than guess. |
| 2250 | if (ref.language === 'arkts' && ref.referenceName.startsWith('.')) { |
| 2251 | const base = ref.referenceName.slice(1); |
| 2252 | const candidates = context |
| 2253 | .getNodesByName(base) |
| 2254 | .filter( |
| 2255 | (n) => |
| 2256 | n.language === 'arkts' && |
| 2257 | n.kind === 'function' && |
| 2258 | (n.decorators ?? []).some((d) => ARKUI_ATTRIBUTE_DECORATORS.has(d)) |
| 2259 | ); |
| 2260 | const chosen = |
| 2261 | candidates.length > 1 ? preferCallSiteFile(candidates, ref.filePath) : candidates; |
| 2262 | if (chosen.length !== 1) return null; |
| 2263 | return { |
| 2264 | original: ref, |
| 2265 | targetNodeId: chosen[0]!.id, |
| 2266 | confidence: 0.85, |
| 2267 | resolvedBy: 'exact-match', |
| 2268 | }; |
| 2269 | } |
| 2270 | |
| 2271 | // Erlang `-behaviour(m)` refs target a MODULE. Letting them fall through to |
| 2272 | // bare-name matching grabs any same-named symbol — on emqx, |
| 2273 | // `-behaviour(supervisor)` resolved to a `-define(supervisor, …)` macro |
| 2274 | // constant in an unrelated app. Resolve only to the behaviour module's |
| 2275 | // namespace; an out-of-repo behaviour (OTP's gen_server/supervisor) stays |
| 2276 | // unresolved rather than guessed. The same module-only rule applies to every |
| 2277 | // ref an `.app`/`.app.src` resource file emits — its `{mod, …}` callback and |
| 2278 | // `{applications, …}` dependency names can only mean modules, and on emqx |
| 2279 | // the `ssl` OTP app otherwise resolved to a test helper FUNCTION named ssl. |
| 2280 | if ( |
| 2281 | ref.language === 'erlang' && |
| 2282 | (ref.referenceKind === 'implements' || /\.app(?:\.src)?$/i.test(ref.filePath)) |
| 2283 | ) { |
| 2284 | const modules = context |
| 2285 | .getNodesByName(ref.referenceName) |
| 2286 | .filter((n) => n.language === 'erlang' && n.kind === 'namespace'); |
no test coverage detected