Skip to content

Commit f0407a4

Browse files
committed
Fix incorrect results caused by conflicting extensions
1 parent e1eae45 commit f0407a4

File tree

9 files changed

+38
-40
lines changed

9 files changed

+38
-40
lines changed

.swiftformat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
--swiftversion 6.0
22

3+
--exclude **/.build/
34
--exclude Tests/Fixtures/
45
--exclude Tests/AccessibilityTests/AccessibilityProject/
56
--exclude Tests/XcodeTests/UIKitProject/

.swiftlint.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
excluded:
2-
- .build
2+
- "**/.build"
33
- Tests/Fixtures
44
- Tests/AccessibilityTests/AccessibilityProject
55
- Tests/XcodeTests/UIKitProject

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Fix building with Bazel on Linux by excluding Xcode support.
1616
- Fix slow baseline filtering for large projects with many results.
1717
- Fix inconsistent unused parameter results when the function is declared in a file that is a member of multiple targets.
18+
- Fix inconsistent results caused by identical extensions declared in different modules.
1819

1920
## 3.1.0 (2025-04-05)
2021

Sources/Frontend/Scan.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ final class Scan {
1717
self.configuration = configuration
1818
self.logger = logger
1919
self.swiftVersion = swiftVersion
20-
graph = SourceGraph(configuration: configuration)
20+
graph = SourceGraph(configuration: configuration, logger: logger)
2121
}
2222

2323
func perform(project: Project) throws -> [ScanResult] {

Sources/Indexer/SwiftIndexer.swift

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ final class SwiftIndexer: Indexer {
251251
private var referencesByUsr: [String: Set<Reference>] = [:]
252252
private var danglingReferences: [Reference] = []
253253
private var varParameterUsrs: Set<String> = []
254+
private var extensionUsrMap: [String: String] = [:]
254255

255256
private func establishDeclarationHierarchy() {
256257
graph.withLock {
@@ -366,7 +367,6 @@ final class SwiftIndexer: Indexer {
366367
decl.modifiers = Set(result.modifiers)
367368
decl.commentCommands = Set(result.commentCommands)
368369
decl.declaredType = result.variableType
369-
decl.hasCapitalSelfFunctionCall = result.hasCapitalSelfFunctionCall
370370
decl.hasGenericFunctionReturnedMetatypeParameters = result.hasGenericFunctionReturnedMetatypeParameters
371371

372372
for ref in decl.references.union(decl.related) {
@@ -487,6 +487,17 @@ final class SwiftIndexer: Indexer {
487487
return nil
488488
}
489489

490+
var usr = usr
491+
492+
if kind.isExtensionKind {
493+
// Identical extensions in different modules have the same USR, which leads to conflicts and incorrect
494+
// results. Here we append the module names to form a unique USR. The only references to this
495+
// extension will exist in the same file, so we only need a file-local mapping for the USR.
496+
let newUsr = "\(usr)-\(location.file.modules.sorted().joined(separator: "-"))"
497+
extensionUsrMap[usr] = newUsr
498+
usr = newUsr
499+
}
500+
490501
let decl = RawDeclaration(
491502
usr: usr,
492503
kind: kind,
@@ -526,6 +537,10 @@ final class SwiftIndexer: Indexer {
526537
for rel in relations {
527538
if rel.roles.contains(.childOf) {
528539
if let parentUsr = rel.symbol.usr {
540+
var parentUsr = parentUsr
541+
if rel.symbol.kind == .extension {
542+
parentUsr = extensionUsrMap[parentUsr] ?? parentUsr
543+
}
529544
childDeclsByParentUsr[parentUsr, default: []].insert(decl)
530545
}
531546
}

Sources/SourceGraph/Elements/Declaration.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ public final class Declaration {
224224
public var commentCommands: Set<CommentCommand> = []
225225
public var references: Set<Reference> = []
226226
public var declaredType: String?
227-
public var hasCapitalSelfFunctionCall: Bool = false
228227
public var hasGenericFunctionReturnedMetatypeParameters: Bool = false
229228
public var parent: Declaration?
230229
public var related: Set<Reference> = []

Sources/SourceGraph/Mutators/ExtensionReferenceBuilder.swift

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ final class ExtensionReferenceBuilder: SourceGraphMutator {
3434
extendedDeclaration.references.formUnion(extensionDeclaration.references)
3535
extendedDeclaration.related.formUnion(extensionDeclaration.related)
3636

37-
if extensionDeclaration.hasCapitalSelfFunctionCall {
38-
extendedDeclaration.hasCapitalSelfFunctionCall = true
39-
}
40-
4137
extensionDeclaration.declarations.forEach { $0.parent = extendedDeclaration }
4238
extensionDeclaration.references.forEach { $0.parent = extendedDeclaration }
4339
extensionDeclaration.related.forEach { $0.parent = extendedDeclaration }

Sources/SourceGraph/SourceGraph.swift

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Configuration
22
import Foundation
3+
import Logger
34
import Shared
45

56
public final class SourceGraph {
@@ -26,9 +27,11 @@ public final class SourceGraph {
2627
private var moduleToExportingModules: [String: Set<String>] = [:]
2728

2829
private let configuration: Configuration
30+
private let logger: Logger
2931

30-
public init(configuration: Configuration) {
32+
public init(configuration: Configuration, logger: Logger) {
3133
self.configuration = configuration
34+
self.logger = logger
3235
}
3336

3437
public func indexingComplete() {
@@ -103,16 +106,22 @@ public final class SourceGraph {
103106
public func add(_ declaration: Declaration) {
104107
allDeclarations.insert(declaration)
105108
allDeclarationsByKind[declaration.kind, default: []].insert(declaration)
106-
declaration.usrs.forEach { allDeclarationsByUsr[$0] = declaration }
109+
for usr in declaration.usrs {
110+
if let existingDecl = allDeclarationsByUsr[usr] {
111+
logger.warn("""
112+
Declaration conflict detected: a declaration with the USR '\(usr)' has already been indexed.
113+
This issue can cause inconsistent and incorrect results.
114+
Existing declaration: \(existingDecl), declared in modules: \(existingDecl.location.file.modules.sorted())
115+
Conflicting declaration: \(declaration), declared in modules: \(declaration.location.file.modules.sorted())
116+
To resolve this warning, make sure all build modules are uniquely named.
117+
""")
118+
}
119+
allDeclarationsByUsr[usr] = declaration
120+
}
107121
}
108122

109123
public func add(_ declarations: Set<Declaration>) {
110-
allDeclarations.formUnion(declarations)
111-
112-
for declaration in declarations {
113-
allDeclarationsByKind[declaration.kind, default: []].insert(declaration)
114-
declaration.usrs.forEach { allDeclarationsByUsr[$0] = declaration }
115-
}
124+
declarations.forEach { add($0) }
116125
}
117126

118127
public func remove(_ declaration: Declaration) {
@@ -258,7 +267,7 @@ public final class SourceGraph {
258267
func extendedDeclaration(forExtension extensionDeclaration: Declaration) throws -> Declaration? {
259268
guard let extendedReference = try extendedDeclarationReference(forExtension: extensionDeclaration) else { return nil }
260269

261-
if let extendedDeclaration = allDeclarationsByUsr[extendedReference.usr] {
270+
if let extendedDeclaration = declaration(withUsr: extendedReference.usr) {
262271
return extendedDeclaration
263272
}
264273

Sources/SyntaxAnalysis/DeclarationSyntaxVisitor.swift

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,12 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
1919
variableInitFunctionCallLocations: Set<Location>,
2020
functionCallMetatypeArgumentLocations: Set<Location>,
2121
typeInitializerLocations: Set<Location>,
22-
hasCapitalSelfFunctionCall: Bool,
2322
hasGenericFunctionReturnedMetatypeParameters: Bool
2423
)
2524

2625
private let sourceLocationBuilder: SourceLocationBuilder
2726
private let typeSyntaxInspector: TypeSyntaxInspector
2827
private(set) var results: [Result] = []
29-
private var didVisitCapitalSelfFunctionCall: Bool = false
3028

3129
public var resultsByLocation: [Location: Result] {
3230
results.reduce(into: [Location: Result]()) { dict, result in
@@ -47,7 +45,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
4745
inheritanceClause: node.inheritanceClause,
4846
genericParameterClause: node.genericParameterClause,
4947
genericWhereClause: node.genericWhereClause,
50-
consumeCapitalSelfFunctionCalls: true,
5148
at: node.name.positionAfterSkippingLeadingTrivia
5249
)
5350
}
@@ -60,7 +57,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
6057
inheritanceClause: node.inheritanceClause,
6158
genericParameterClause: node.genericParameterClause,
6259
genericWhereClause: node.genericWhereClause,
63-
consumeCapitalSelfFunctionCalls: true,
6460
at: node.name.positionAfterSkippingLeadingTrivia
6561
)
6662
}
@@ -84,7 +80,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
8480
inheritanceClause: node.inheritanceClause,
8581
genericParameterClause: node.genericParameterClause,
8682
genericWhereClause: node.genericWhereClause,
87-
consumeCapitalSelfFunctionCalls: true,
8883
at: node.name.positionAfterSkippingLeadingTrivia
8984
)
9085
}
@@ -131,7 +126,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
131126
trivia: node.commentCommandTrivia,
132127
inheritanceClause: node.inheritanceClause,
133128
genericWhereClause: node.genericWhereClause,
134-
consumeCapitalSelfFunctionCalls: true,
135129
at: position
136130
)
137131
}
@@ -288,14 +282,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
288282
)
289283
}
290284

291-
public func visit(_ node: FunctionCallExprSyntax) {
292-
if let identifierExpr = node.calledExpression.as(DeclReferenceExprSyntax.self),
293-
identifierExpr.baseName.tokenKind == .keyword(.Self)
294-
{
295-
didVisitCapitalSelfFunctionCall = true
296-
}
297-
}
298-
299285
// MARK: - Private
300286

301287
private func parse(
@@ -312,7 +298,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
312298
genericWhereClause: GenericWhereClauseSyntax? = nil,
313299
variableInitFunctionCallExpr: FunctionCallExprSyntax? = nil,
314300
typeInitializerClause: TypeInitializerClauseSyntax? = nil,
315-
consumeCapitalSelfFunctionCalls: Bool = false,
316301
at position: AbsolutePosition
317302
) {
318303
let modifierNames = modifiers?.map(\.name.text) ?? []
@@ -321,13 +306,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
321306
AttributeSyntax($0)?.attributeName.trimmedDescription ?? AttributeSyntax($0)?.attributeName.firstToken(viewMode: .sourceAccurate)?.text
322307
} ?? []
323308
let location = sourceLocationBuilder.location(at: position)
324-
325-
var didVisitCapitalSelfFunctionCall = false
326-
if consumeCapitalSelfFunctionCalls {
327-
didVisitCapitalSelfFunctionCall = self.didVisitCapitalSelfFunctionCall
328-
self.didVisitCapitalSelfFunctionCall = false
329-
}
330-
331309
let returnClauseTypeLocations = typeNameLocations(for: returnClause)
332310
let parameterClauseTypes = parameterClause?.parameters.map(\.type) ?? []
333311
let closureParameterClauseTypes = closureParameterClause?.parameters.compactMap(\.type) ?? []
@@ -360,7 +338,6 @@ public final class DeclarationSyntaxVisitor: PeripherySyntaxVisitor {
360338
variableInitFunctionCallLocations: locations(for: variableInitFunctionCallExpr),
361339
functionCallMetatypeArgumentLocations: functionCallMetatypeArgumentLocations(for: variableInitFunctionCallExpr),
362340
typeInitializerLocations: typeLocations(for: typeInitializerClause?.value),
363-
hasCapitalSelfFunctionCall: didVisitCapitalSelfFunctionCall,
364341
hasGenericFunctionReturnedMetatypeParameters: hasGenericFunctionReturnedMetatypeParameters
365342
))
366343
}

0 commit comments

Comments
 (0)