( ref: UnresolvedRef, context: ResolutionContext )
| 1305 | } |
| 1306 | |
| 1307 | export function resolveViaImport( |
| 1308 | ref: UnresolvedRef, |
| 1309 | context: ResolutionContext |
| 1310 | ): ResolvedRef | null { |
| 1311 | // C/C++ #include references — resolve directly to the included file |
| 1312 | // (file→file edge), bypassing symbol lookup. The extractor emits these |
| 1313 | // with `referenceKind: 'imports'` and `referenceName: <include path>` |
| 1314 | // (e.g. "uint256.h" or "common/args.h"). Without this branch the |
| 1315 | // include-dir scan path inside resolveImportPath never produces an |
| 1316 | // edge — resolveViaImport's symbol lookup below would search the |
| 1317 | // resolved file for a symbol named like the file extension and fail. |
| 1318 | if ((ref.language === 'c' || ref.language === 'cpp') && ref.referenceKind === 'imports') { |
| 1319 | // C/C++ quoted includes (`#include "X.h"`) resolve relative to the |
| 1320 | // INCLUDING file's own directory first (the C standard's quoted-include |
| 1321 | // search order). Prefer a same-directory header over an -I directory or a |
| 1322 | // same-named header on another platform (windows/code/RNCAsyncStorage.h vs |
| 1323 | // apple/.../RNCAsyncStorage.h) — the include-dir heuristic below would |
| 1324 | // otherwise pick an arbitrary same-named header, leaving the real local one |
| 1325 | // with no dependents. |
| 1326 | const slash = ref.filePath.lastIndexOf('/'); |
| 1327 | const fromDir = slash >= 0 ? ref.filePath.slice(0, slash) : ''; |
| 1328 | const siblingPath = path.posix.normalize(fromDir ? `${fromDir}/${ref.referenceName}` : ref.referenceName); |
| 1329 | const siblingBase = siblingPath.split('/').pop()!; |
| 1330 | const sibling = context |
| 1331 | .getNodesByName(siblingBase) |
| 1332 | .find((n) => n.kind === 'file' && n.filePath === siblingPath); |
| 1333 | if (sibling) { |
| 1334 | return { original: ref, targetNodeId: sibling.id, confidence: 0.92, resolvedBy: 'import' }; |
| 1335 | } |
| 1336 | const resolvedPath = resolveImportPath(ref.referenceName, ref.filePath, ref.language, context); |
| 1337 | if (!resolvedPath) return null; |
| 1338 | const basename = resolvedPath.split('/').pop()!; |
| 1339 | const fileNodes = context.getNodesByName(basename).filter((n) => n.kind === 'file'); |
| 1340 | const fileNode = fileNodes.find((n) => n.filePath === resolvedPath); |
| 1341 | if (fileNode) { |
| 1342 | return { |
| 1343 | original: ref, |
| 1344 | targetNodeId: fileNode.id, |
| 1345 | confidence: 0.9, |
| 1346 | resolvedBy: 'import', |
| 1347 | }; |
| 1348 | } |
| 1349 | return null; |
| 1350 | } |
| 1351 | |
| 1352 | // COBOL COPY / EXEC SQL INCLUDE — resolve the copybook member to a |
| 1353 | // file→file edge, mirroring the C/C++ include branch above. A member that |
| 1354 | // matches no indexed file (compiler-supplied copybooks like SQLCA/DFHAID) |
| 1355 | // stays unresolved — callers must not fall back to the symbol name-matcher, |
| 1356 | // which would connect it to a same-named import symbol elsewhere. |
| 1357 | if (isCobolCopybookRef(ref)) { |
| 1358 | const resolvedPath = resolveImportPath(ref.referenceName, ref.filePath, ref.language!, context); |
| 1359 | if (!resolvedPath) return null; |
| 1360 | const basename = resolvedPath.split('/').pop()!; |
| 1361 | const fileNode = context |
| 1362 | .getNodesByName(basename) |
| 1363 | .find((n) => n.kind === 'file' && n.filePath === resolvedPath); |
| 1364 | if (fileNode) { |
no test coverage detected