@@ -33,7 +33,7 @@ public actor PackageParser {
3333 let dependencies = try extractDependencies ( from: content)
3434
3535 // Extract targets
36- let targets = try extractTargets ( from: content)
36+ let targets = try extractTargets ( from: content, packageContent : content )
3737
3838 return PackageInfo (
3939 name: packageName,
@@ -89,7 +89,7 @@ public actor PackageParser {
8989 return dependencies
9090 }
9191
92- private func extractTargets( from content: String ) throws -> [ Target ] {
92+ private func extractTargets( from content: String , packageContent : String ) throws -> [ Target ] {
9393 var targets : [ Target ] = [ ]
9494
9595 // Try to extract targets from both patterns:
@@ -117,12 +117,12 @@ public actor PackageParser {
117117 for match in targetsSection. matches ( of: executableRegex) {
118118 let name = String ( match. 1 )
119119 let dependenciesStr = String ( match. 2 )
120- let dependencies = parseDependencyList ( dependenciesStr)
120+ let dependencyInfo = parseDependencyListWithLineNumbers ( dependenciesStr, targetName : name , packageContent : packageContent )
121121
122122 targets. append ( Target (
123123 name: name,
124124 type: . executable,
125- dependencies : dependencies ,
125+ dependencyInfo : dependencyInfo ,
126126 path: nil
127127 ) )
128128 }
@@ -131,12 +131,12 @@ public actor PackageParser {
131131 for match in targetsSection. matches ( of: libraryRegex) {
132132 let name = String ( match. 1 )
133133 let dependenciesStr = String ( match. 2 )
134- let dependencies = parseDependencyList ( dependenciesStr)
134+ let dependencyInfo = parseDependencyListWithLineNumbers ( dependenciesStr, targetName : name , packageContent : packageContent )
135135
136136 targets. append ( Target (
137137 name: name,
138138 type: . library,
139- dependencies : dependencies ,
139+ dependencyInfo : dependencyInfo ,
140140 path: nil
141141 ) )
142142 }
@@ -145,12 +145,12 @@ public actor PackageParser {
145145 for match in targetsSection. matches ( of: testRegex) {
146146 let name = String ( match. 1 )
147147 let dependenciesStr = String ( match. 2 )
148- let dependencies = parseDependencyList ( dependenciesStr)
148+ let dependencyInfo = parseDependencyListWithLineNumbers ( dependenciesStr, targetName : name , packageContent : packageContent )
149149
150150 targets. append ( Target (
151151 name: name,
152152 type: . test,
153- dependencies : dependencies ,
153+ dependencyInfo : dependencyInfo ,
154154 path: nil
155155 ) )
156156 }
@@ -159,12 +159,12 @@ public actor PackageParser {
159159 for match in targetsSection. matches ( of: macroRegex) {
160160 let name = String ( match. 1 )
161161 let dependenciesStr = String ( match. 2 )
162- let dependencies = parseDependencyList ( dependenciesStr)
162+ let dependencyInfo = parseDependencyListWithLineNumbers ( dependenciesStr, targetName : name , packageContent : packageContent )
163163
164164 targets. append ( Target (
165165 name: name,
166166 type: . library, // Treat macros as library targets for dependency analysis
167- dependencies : dependencies ,
167+ dependencyInfo : dependencyInfo ,
168168 path: nil
169169 ) )
170170 }
@@ -197,12 +197,12 @@ public actor PackageParser {
197197 for match in targetsSection. matches ( of: pluginRegex) {
198198 let name = String ( match. 1 )
199199 let dependenciesStr = String ( match. 2 )
200- let dependencies = parseDependencyList ( dependenciesStr)
200+ let dependencyInfo = parseDependencyListWithLineNumbers ( dependenciesStr, targetName : name , packageContent : packageContent )
201201
202202 targets. append ( Target (
203203 name: name,
204204 type: . plugin,
205- dependencies : dependencies ,
205+ dependencyInfo : dependencyInfo ,
206206 path: nil
207207 ) )
208208 }
@@ -383,6 +383,79 @@ public actor PackageParser {
383383 return dependencies
384384 }
385385
386+ private func parseDependencyListWithLineNumbers( _ dependenciesStr: String , targetName: String , packageContent: String ) -> [ DependencyInfo ] {
387+ var dependencyInfos : [ DependencyInfo ] = [ ]
388+
389+ // Get the line numbers for dependencies by searching in the full package content
390+ let dependencies = parseDependencyList ( dependenciesStr)
391+
392+ for dependency in dependencies {
393+ if let lineNumber = findDependencyLineNumber ( dependency: dependency, targetName: targetName, in: packageContent) {
394+ dependencyInfos. append ( DependencyInfo ( name: dependency, lineNumber: lineNumber) )
395+ } else {
396+ dependencyInfos. append ( DependencyInfo ( name: dependency, lineNumber: nil ) )
397+ }
398+ }
399+
400+ return dependencyInfos
401+ }
402+
403+ private func findDependencyLineNumber( dependency: String , targetName: String , in content: String ) -> Int ? {
404+ let lines = content. components ( separatedBy: . newlines)
405+
406+ // Find the target declaration first
407+ var targetStartLine : Int ?
408+ var targetEndLine : Int ?
409+ var braceDepth = 0
410+ var inTarget = false
411+
412+ for (index, line) in lines. enumerated ( ) {
413+ if line. contains ( " name: \" \( targetName) \" " ) && ( line. contains ( " .target " ) || line. contains ( " .executableTarget " ) || line. contains ( " .testTarget " ) || line. contains ( " .macro " ) || line. contains ( " .plugin " ) ) {
414+ targetStartLine = index + 1
415+ inTarget = true
416+ braceDepth = 0
417+ }
418+
419+ if inTarget {
420+ // Count braces to determine where the target declaration ends
421+ for char in line {
422+ if char == " ( " {
423+ braceDepth += 1
424+ } else if char == " ) " {
425+ braceDepth -= 1
426+ if braceDepth == 0 {
427+ targetEndLine = index + 1
428+ break
429+ }
430+ }
431+ }
432+
433+ if let targetEndLine = targetEndLine {
434+ break
435+ }
436+ }
437+ }
438+
439+ // Search for the dependency within the target declaration
440+ if let startLine = targetStartLine, let endLine = targetEndLine {
441+ for lineIndex in ( startLine - 1 ) ..< min ( endLine, lines. count) {
442+ let line = lines [ lineIndex]
443+ // Look for the dependency name in quotes or as a product name
444+ if line. contains ( " \" \( dependency) \" " ) {
445+ return lineIndex + 1
446+ }
447+ }
448+ }
449+
450+ return nil
451+ }
452+
453+ private func findLineNumber( for range: Range < String . Index > , in content: String ) -> Int ? {
454+ let prefix = content [ ..< range. lowerBound]
455+ let lineNumber = prefix. components ( separatedBy: . newlines) . count
456+ return lineNumber
457+ }
458+
386459 private func repoNameToModuleName( _ repoName: String ) -> String {
387460 // Convert common repository naming patterns to module names
388461 let cleanedName = repoName
0 commit comments