Skip to content

Commit d5f8fc8

Browse files
committed
Check that there are no duplicate code completion results
Code completion shouldn’t return the same result twice. rdar://92081219
1 parent 8275f72 commit d5f8fc8

File tree

2 files changed

+36
-5
lines changed

2 files changed

+36
-5
lines changed

SourceKitStressTester/Sources/Common/Message.swift

+10-3
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,14 @@ public enum SourceKitError: Error {
7676
}
7777
}
7878

79-
public enum SourceKitErrorReason: String, Codable {
80-
case errorResponse, errorTypeInResponse, errorDeserializingSyntaxTree,
81-
sourceAndSyntaxTreeMismatch, missingExpectedResult, errorWritingModule
79+
public enum SourceKitErrorReason: Codable {
80+
case errorResponse
81+
case errorTypeInResponse
82+
case errorDeserializingSyntaxTree
83+
case sourceAndSyntaxTreeMismatch
84+
case missingExpectedResult
85+
case duplicateResult(name: String)
86+
case errorWritingModule
8287
}
8388

8489
public enum RequestInfo {
@@ -477,6 +482,8 @@ extension SourceKitErrorReason: CustomStringConvertible {
477482
return "SourceKit returned a syntax tree that doesn't match the expected source"
478483
case .missingExpectedResult:
479484
return "SourceKit returned a response that didn't contain the expected result"
485+
case .duplicateResult(name: let name):
486+
return "SourceKit returned a response that contained the result '\(name)' twice"
480487
case .errorWritingModule:
481488
return "Error while writing out module"
482489
}

SourceKitStressTester/Sources/StressTester/SourceKitDocument.swift

+26-2
Original file line numberDiff line numberDiff line change
@@ -425,21 +425,45 @@ class SourceKitDocument {
425425
}
426426

427427
private func checkExpectedCompletionResult(_ expected: ExpectedResult, in response: SourceKitdResponse, info: RequestInfo) throws {
428+
/// Struct that defines a code completion result to determine if there are duplicate results.
429+
struct CodeCompletionResultSpec: Hashable {
430+
let description: String
431+
let type: String
432+
let usr: String
433+
434+
init(_ result: SourceKitdResponse.Dictionary) {
435+
self.description = result.getString(.key_Description)
436+
self.type = result.getString(.key_TypeName)
437+
self.usr = result.getString(.key_AssociatedUSRs)
438+
}
439+
}
428440
let matcher = CompletionMatcher(for: expected)
429441
var found = false
442+
var foundDuplicate: CodeCompletionResultSpec? = nil
443+
var seenResults = Set<CodeCompletionResultSpec>()
430444
response.value.getArray(.key_Results).enumerate { (_, item) -> Bool in
431445
let result = item.getDictionary()
432446
found = matcher.match(result.getString(.key_Name), ignoreArgLabels: shouldIgnoreArgs(of: expected, for: result))
447+
let resultSpec = CodeCompletionResultSpec(result)
448+
if foundDuplicate == nil, !seenResults.insert(resultSpec).inserted {
449+
foundDuplicate = resultSpec
450+
}
433451
return !found
434452
}
435-
if !found {
453+
lazy var truncatedResponseText = {
436454
// FIXME: code completion responses can be huge, truncate them for now.
437455
let maxSize = 25_000
438456
var responseText = response.description
439457
if responseText.count > maxSize {
440458
responseText = responseText.prefix(maxSize) + "[truncated]"
441459
}
442-
throw SourceKitError.failed(.missingExpectedResult, request: info, response: responseText.trimmingCharacters(in: .newlines))
460+
return responseText
461+
}()
462+
if !found {
463+
throw SourceKitError.failed(.missingExpectedResult, request: info, response: truncatedResponseText.trimmingCharacters(in: .newlines))
464+
}
465+
if let foundDuplicate = foundDuplicate {
466+
throw SourceKitError.failed(.duplicateResult(name: foundDuplicate.description), request: info, response: truncatedResponseText.trimmingCharacters(in: .newlines))
443467
}
444468
}
445469

0 commit comments

Comments
 (0)