( ref: UnresolvedRef, context: ResolutionContext )
| 1121 | } |
| 1122 | |
| 1123 | export function resolveViaImport( |
| 1124 | ref: UnresolvedRef, |
| 1125 | context: ResolutionContext |
| 1126 | ): ResolvedRef | null { |
| 1127 | // C/C++ #include references — resolve directly to the included file |
| 1128 | // (file→file edge), bypassing symbol lookup. The extractor emits these |
| 1129 | // with `referenceKind: 'imports'` and `referenceName: <include path>` |
| 1130 | // (e.g. "uint256.h" or "common/args.h"). Without this branch the |
| 1131 | // include-dir scan path inside resolveImportPath never produces an |
| 1132 | // edge — resolveViaImport's symbol lookup below would search the |
| 1133 | // resolved file for a symbol named like the file extension and fail. |
| 1134 | if ((ref.language === 'c' || ref.language === 'cpp') && ref.referenceKind === 'imports') { |
| 1135 | // C/C++ quoted includes (`#include "X.h"`) resolve relative to the |
| 1136 | // INCLUDING file's own directory first (the C standard's quoted-include |
| 1137 | // search order). Prefer a same-directory header over an -I directory or a |
| 1138 | // same-named header on another platform (windows/code/RNCAsyncStorage.h vs |
| 1139 | // apple/.../RNCAsyncStorage.h) — the include-dir heuristic below would |
| 1140 | // otherwise pick an arbitrary same-named header, leaving the real local one |
| 1141 | // with no dependents. |
| 1142 | const slash = ref.filePath.lastIndexOf('/'); |
| 1143 | const fromDir = slash >= 0 ? ref.filePath.slice(0, slash) : ''; |
| 1144 | const siblingPath = path.posix.normalize(fromDir ? `${fromDir}/${ref.referenceName}` : ref.referenceName); |
| 1145 | const siblingBase = siblingPath.split('/').pop()!; |
| 1146 | const sibling = context |
| 1147 | .getNodesByName(siblingBase) |
| 1148 | .find((n) => n.kind === 'file' && n.filePath === siblingPath); |
| 1149 | if (sibling) { |
| 1150 | return { original: ref, targetNodeId: sibling.id, confidence: 0.92, resolvedBy: 'import' }; |
| 1151 | } |
| 1152 | const resolvedPath = resolveImportPath(ref.referenceName, ref.filePath, ref.language, context); |
| 1153 | if (!resolvedPath) return null; |
| 1154 | const basename = resolvedPath.split('/').pop()!; |
| 1155 | const fileNodes = context.getNodesByName(basename).filter((n) => n.kind === 'file'); |
| 1156 | const fileNode = fileNodes.find((n) => n.filePath === resolvedPath); |
| 1157 | if (fileNode) { |
| 1158 | return { |
| 1159 | original: ref, |
| 1160 | targetNodeId: fileNode.id, |
| 1161 | confidence: 0.9, |
| 1162 | resolvedBy: 'import', |
| 1163 | }; |
| 1164 | } |
| 1165 | return null; |
| 1166 | } |
| 1167 | |
| 1168 | // PHP include/require — resolve the static string path to a file→file |
| 1169 | // edge, mirroring the C/C++ branch above. Distinguish include PATHS from |
| 1170 | // namespace `use` symbols by shape: an include path contains a slash or a |
| 1171 | // file extension ("lib.php", "inc/db.php", "../x.php"), whereas a namespace |
| 1172 | // use is an FQN (App\Foo\Bar) or a bare class symbol (Closure) — PHP |
| 1173 | // identifiers contain neither '/' nor '.'. Only path-shaped references are |
| 1174 | // includes; symbol references fall through to the namespace resolution. |
| 1175 | if (isPhpIncludePathRef(ref)) { |
| 1176 | const resolvedPath = resolvePhpIncludePath(ref.referenceName, ref.filePath, context); |
| 1177 | if (resolvedPath) { |
| 1178 | const basename = resolvedPath.split('/').pop()!; |
| 1179 | const fileNode = context |
| 1180 | .getNodesByName(basename) |
no test coverage detected