11
11
//===----------------------------------------------------------------------===//
12
12
13
13
import Foundation
14
+ import LSPLogging
14
15
15
16
// MARK: - Entry point
16
17
@@ -48,60 +49,65 @@ fileprivate class CommandLineArgumentReducer {
48
49
self . progressUpdate = progressUpdate
49
50
}
50
51
51
- func logSuccessfulReduction ( _ requestInfo : RequestInfo ) {
52
- progressUpdate (
53
- 1 - ( Double ( requestInfo. compilerArgs . count ) / Double ( initialCommandLineCount ) ) ,
54
- " Reduced compiler arguments to \( requestInfo. compilerArgs . count ) "
55
- )
52
+ func run ( initialRequestInfo : RequestInfo ) async throws -> RequestInfo {
53
+ var requestInfo = initialRequestInfo
54
+ requestInfo = try await reduce ( initialRequestInfo : requestInfo, simultaneousRemove : 10 )
55
+ requestInfo = try await reduce ( initialRequestInfo : requestInfo, simultaneousRemove : 1 )
56
+ return requestInfo
56
57
}
57
58
58
- func run( initialRequestInfo: RequestInfo ) async throws -> RequestInfo {
59
+ /// Reduce the command line arguments of the given `RequestInfo`.
60
+ ///
61
+ /// If `simultaneousRemove` is set, the reducer will try to remove that many arguments at once. This is useful to
62
+ /// quickly remove multiple arguments from the request.
63
+ private func reduce( initialRequestInfo: RequestInfo , simultaneousRemove: Int ) async throws -> RequestInfo {
64
+ guard initialRequestInfo. compilerArgs. count > simultaneousRemove else {
65
+ // Trying to remove more command line arguments than we have. This isn't going to work.
66
+ return initialRequestInfo
67
+ }
68
+
59
69
var requestInfo = initialRequestInfo
60
70
self . initialCommandLineCount = requestInfo. compilerArgs. count
61
71
62
72
var argumentIndexToRemove = requestInfo. compilerArgs. count - 1
63
- while argumentIndexToRemove >= 0 {
64
- var numberOfArgumentsToRemove = 1
73
+ while argumentIndexToRemove + 1 >= simultaneousRemove {
74
+ defer {
75
+ // argumentIndexToRemove can become negative by being decremented in the code below
76
+ let progress = 1 - ( Double ( max ( argumentIndexToRemove, 0 ) ) / Double( initialCommandLineCount) )
77
+ progressUpdate ( progress, " Reduced compiler arguments to \( requestInfo. compilerArgs. count) " )
78
+ }
79
+ var numberOfArgumentsToRemove = simultaneousRemove
65
80
// If the argument is preceded by -Xswiftc or -Xcxx, we need to remove the `-X` flag as well.
66
- if argumentIndexToRemove - numberOfArgumentsToRemove >= 0
67
- && requestInfo. compilerArgs [ argumentIndexToRemove - numberOfArgumentsToRemove] . hasPrefix ( " -X " )
68
- {
81
+ if requestInfo. compilerArgs [ safe: argumentIndexToRemove - numberOfArgumentsToRemove] ? . hasPrefix ( " -X " ) ?? false {
69
82
numberOfArgumentsToRemove += 1
70
83
}
71
84
72
- if let reduced = try await tryRemoving (
73
- ( argumentIndexToRemove - numberOfArgumentsToRemove + 1 ) ... argumentIndexToRemove,
74
- from: requestInfo
75
- ) {
85
+ let rangeToRemove = ( argumentIndexToRemove - numberOfArgumentsToRemove + 1 ) ... argumentIndexToRemove
86
+ if let reduced = try await tryRemoving ( rangeToRemove, from: requestInfo) {
76
87
requestInfo = reduced
77
88
argumentIndexToRemove -= numberOfArgumentsToRemove
78
89
continue
79
90
}
80
91
81
- // If removing the argument failed and the argument is preceded by an argument starting with `-`, try removing that as well.
82
- // E.g. removing `-F` followed by a search path.
83
- if argumentIndexToRemove - numberOfArgumentsToRemove >= 0
84
- && requestInfo. compilerArgs [ argumentIndexToRemove - numberOfArgumentsToRemove] . hasPrefix ( " - " )
85
- {
92
+ // If removing the argument failed and the argument is preceded by an argument starting with `-`, try removing
93
+ // that as well. E.g. removing `-F` followed by a search path.
94
+ if requestInfo. compilerArgs [ safe: argumentIndexToRemove - numberOfArgumentsToRemove] ? . hasPrefix ( " - " ) ?? false {
86
95
numberOfArgumentsToRemove += 1
87
- }
88
96
89
- // If the argument is preceded by -Xswiftc or -Xcxx, we need to remove the `-X` flag as well.
90
- if argumentIndexToRemove - numberOfArgumentsToRemove >= 0
91
- && requestInfo. compilerArgs [ argumentIndexToRemove - numberOfArgumentsToRemove] . hasPrefix ( " -X " )
92
- {
93
- numberOfArgumentsToRemove += 1
97
+ // If the argument is preceded by -Xswiftc or -Xcxx, we need to remove the `-X` flag as well.
98
+ if requestInfo. compilerArgs [ safe: argumentIndexToRemove - numberOfArgumentsToRemove] ? . hasPrefix ( " -X " ) ?? false {
99
+ numberOfArgumentsToRemove += 1
100
+ }
101
+
102
+ let rangeToRemove = ( argumentIndexToRemove - numberOfArgumentsToRemove + 1 ) ... argumentIndexToRemove
103
+ if let reduced = try await tryRemoving ( rangeToRemove, from: requestInfo) {
104
+ requestInfo = reduced
105
+ argumentIndexToRemove -= numberOfArgumentsToRemove
106
+ continue
107
+ }
94
108
}
95
109
96
- if let reduced = try await tryRemoving (
97
- ( argumentIndexToRemove - numberOfArgumentsToRemove + 1 ) ... argumentIndexToRemove,
98
- from: requestInfo
99
- ) {
100
- requestInfo = reduced
101
- argumentIndexToRemove -= numberOfArgumentsToRemove
102
- continue
103
- }
104
- argumentIndexToRemove -= 1
110
+ argumentIndexToRemove -= simultaneousRemove
105
111
}
106
112
107
113
return requestInfo
@@ -111,16 +117,28 @@ fileprivate class CommandLineArgumentReducer {
111
117
_ argumentsToRemove: ClosedRange < Int > ,
112
118
from requestInfo: RequestInfo
113
119
) async throws -> RequestInfo? {
120
+ logger. debug ( " Try removing the following compiler arguments: \n \( requestInfo. compilerArgs [ argumentsToRemove] ) " )
114
121
var reducedRequestInfo = requestInfo
115
122
reducedRequestInfo. compilerArgs. removeSubrange ( argumentsToRemove)
116
123
117
124
let result = try await sourcekitdExecutor. run ( request: reducedRequestInfo)
118
125
if case . reproducesIssue = result {
119
- logSuccessfulReduction ( reducedRequestInfo )
126
+ logger . debug ( " Reduction successful " )
120
127
return reducedRequestInfo
121
128
} else {
122
129
// The reduced request did not crash. We did not find a reduced test case, so return `nil`.
130
+ logger. debug ( " Reduction did not reproduce the issue " )
131
+ return nil
132
+ }
133
+ }
134
+ }
135
+
136
+ fileprivate extension Array {
137
+ /// Access index in the array if it's in bounds or return `nil` if `index` is outside of the array's bounds.
138
+ subscript ( safe index: Int) - > Element? {
139
+ if index < 0 || index >= count {
123
140
return nil
124
141
}
142
+ return self [ index]
125
143
}
126
144
}
0 commit comments