@@ -78,9 +78,21 @@ extension AnyDiagnostic {
78
78
self . level != . note
79
79
}
80
80
81
+ var isNote : Bool {
82
+ !self . isPrimary
83
+ }
84
+
81
85
var isIgnored : Bool {
82
86
self . level == . ignored
83
87
}
88
+
89
+ var hasFixIt : Bool {
90
+ !self . fixIts. isEmpty
91
+ }
92
+
93
+ var hasNoLocation : Bool {
94
+ self . location == nil
95
+ }
84
96
}
85
97
86
98
extension SerializedDiagnostics . Diagnostic : AnyDiagnostic { }
@@ -113,90 +125,92 @@ package struct SwiftFixIt /*: ~Copyable */ {
113
125
)
114
126
}
115
127
116
- init (
117
- diagnostics: some Collection < some AnyDiagnostic > ,
128
+ init < Diagnostic : AnyDiagnostic > (
129
+ diagnostics: some Collection < Diagnostic > ,
118
130
categories: Set < String > ,
119
131
fileSystem: any FileSystem
120
132
) throws {
121
133
self . fileSystem = fileSystem
122
134
123
- // Build a map from source files to `SwiftDiagnostics` diagnostics.
124
- var diagnosticsPerFile : DiagnosticsPerFile = [ : ]
125
-
126
- var diagnosticConverter = DiagnosticConverter ( fileSystem: fileSystem)
127
- var currentPrimaryDiagnosticHasNoteWithFixIt = false
128
-
129
- var index = diagnostics. startIndex
130
- while index != diagnostics. endIndex {
131
- let diagnostic = diagnostics [ index]
132
-
133
- func skipDiagnostic( ) {
134
- guard diagnostic. isPrimary else {
135
- diagnostics. formIndex ( after: & index)
136
- return
137
- }
138
-
139
- // Skip a primary diagnostic together with its notes.
140
- repeat {
141
- diagnostics. formIndex ( after: & index)
142
- } while index != diagnostics. endIndex && !diagnostics[ index] . isPrimary
143
- }
135
+ func shouldSkip( primaryDiagnosticWithNotes: some Collection < Diagnostic > ) -> Bool {
136
+ let diagnostic = primaryDiagnosticWithNotes [ primaryDiagnosticWithNotes. startIndex]
144
137
145
- // Skip ignored diagnostics.
146
- guard !diagnostic. isIgnored else {
147
- skipDiagnostic ( )
148
- continue
138
+ // Skip if ignored.
139
+ if diagnostic. isIgnored {
140
+ return true
149
141
}
150
142
151
- // Skip diagnostics with no location.
152
- guard diagnostic. location != nil else {
153
- skipDiagnostic ( )
154
- continue
143
+ // Skip if no location.
144
+ if diagnostic. hasNoLocation {
145
+ return true
155
146
}
156
147
157
- // Skip a primary diagnostic if categories were given and it does
158
- // not belong to any of them.
159
- if !categories. isEmpty && diagnostic . isPrimary {
148
+ // Skip if categories were given and the diagnostic does not
149
+ // belong to any of them.
150
+ if !categories. isEmpty {
160
151
guard let category = diagnostic. category, categories. contains ( category) else {
161
- skipDiagnostic ( )
162
- continue
152
+ return true
163
153
}
164
154
}
165
155
166
- defer {
167
- diagnostics. formIndex ( after: & index)
156
+ let notes = primaryDiagnosticWithNotes. dropFirst ( )
157
+
158
+ // Consider the diagnostic compromised if a note does not have a
159
+ // location.
160
+ if notes. contains ( where: \. hasNoLocation) {
161
+ return true
168
162
}
169
163
170
- let hasFixits = !diagnostic. fixIts. isEmpty
171
-
172
- if diagnostic. isPrimary {
173
- currentPrimaryDiagnosticHasNoteWithFixIt = false
174
- } else {
175
- if hasFixits {
176
- // The Swift compiler produces parallel fix-its by attaching
177
- // them to notes, which in turn associate to a single
178
- // error/warning. Prefer the first fix-it in this case: if
179
- // the last error/warning we saw has a note with a fix-it
180
- // and so is this one, skip it.
181
- if currentPrimaryDiagnosticHasNoteWithFixIt {
182
- continue
183
- }
184
-
185
- currentPrimaryDiagnosticHasNoteWithFixIt = true
186
- }
164
+ let numberOfNotesWithFixIts = notes. count ( where: \. hasFixIt)
165
+ switch numberOfNotesWithFixIts {
166
+ case 0 :
167
+ // Skip if neither the primary diagnostic nor any of its notes
168
+ // has a fix-it.
169
+ return !diagnostic. hasFixIt
170
+ case 1 :
171
+ return false
172
+ default :
173
+ // Skip if more than 1 note has a fix-it. These diagnostics
174
+ // generally require user intervention.
175
+ // TODO: This will have to done lazier once we support printing them.
176
+ return true
187
177
}
178
+ }
179
+
180
+ // Build a map from source files to `SwiftDiagnostics` diagnostics.
181
+ var diagnosticsPerFile : DiagnosticsPerFile = [ : ]
182
+ var diagnosticConverter = DiagnosticConverter ( fileSystem: fileSystem)
183
+
184
+ var nextPrimaryIndex = diagnostics. startIndex
185
+ while nextPrimaryIndex != diagnostics. endIndex {
186
+ let currentPrimaryIndex = nextPrimaryIndex
187
+ precondition ( diagnostics [ currentPrimaryIndex] . isPrimary)
188
+
189
+ // Shift the index to the next primary diagnostic.
190
+ repeat {
191
+ diagnostics. formIndex ( after: & nextPrimaryIndex)
192
+ } while nextPrimaryIndex != diagnostics. endIndex && diagnostics [ nextPrimaryIndex] . isNote
193
+
194
+ let primaryDiagnosticWithNotes = diagnostics [ currentPrimaryIndex ..< nextPrimaryIndex]
188
195
189
- // We are only interested in diagnostics with fix-its.
190
- guard hasFixits else {
196
+ if shouldSkip ( primaryDiagnosticWithNotes: primaryDiagnosticWithNotes) {
191
197
continue
192
198
}
193
199
194
- let ( sourceFile, convertedDiagnostic) = try diagnosticConverter. diagnostic ( from: diagnostic)
200
+ for diagnostic in primaryDiagnosticWithNotes {
201
+ // We are only interested in diagnostics with fix-its.
202
+ // TODO: This will have to change once we support printing them.
203
+ guard diagnostic. hasFixIt else {
204
+ continue
205
+ }
195
206
196
- diagnosticsPerFile [ consume sourceFile, default: [ ] ] . append ( consume convertedDiagnostic)
207
+ let ( sourceFile, convertedDiagnostic) = try diagnosticConverter. diagnostic ( from: diagnostic)
208
+
209
+ diagnosticsPerFile [ consume sourceFile, default: [ ] ] . append ( consume convertedDiagnostic)
210
+ }
197
211
}
198
212
199
- self . diagnosticsPerFile = consume diagnosticsPerFile
213
+ self . diagnosticsPerFile = diagnosticsPerFile
200
214
}
201
215
202
216
package func applyFixIts( ) throws {
0 commit comments