Skip to content

Commit f106a41

Browse files
committed
Extract diagnostics
1 parent ba9195c commit f106a41

File tree

5 files changed

+160
-114
lines changed

5 files changed

+160
-114
lines changed

Sources/FileCheck/CheckString.swift

Lines changed: 109 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -80,112 +80,7 @@ struct CheckString {
8080
// Match itself from the last position after matching CHECK-DAG.
8181
let matchBuffer = buffer.substring(from: buffer.index(buffer.startIndex, offsetBy: lastPos))
8282
guard let (range, mutVariableTable) = self.pattern.match(matchBuffer, initialTable) else {
83-
if let rtm = self.pattern.computeRegexToMatch(variableTable) {
84-
if !self.pattern.fixedString.isEmpty {
85-
diagnose(.error,
86-
at: self.loc,
87-
with: self.prefix + ": could not find '\(self.pattern.fixedString)' (with regex '\(rtm)') in input",
88-
options: options
89-
)
90-
} else {
91-
diagnose(.error,
92-
at: self.loc,
93-
with: self.prefix + ": could not find a match for regex '\(rtm)' in input",
94-
options: options
95-
)
96-
}
97-
} else {
98-
diagnose(.error,
99-
at: self.loc,
100-
with: self.prefix + ": could not find '\(self.pattern.fixedString)' in input",
101-
options: options
102-
)
103-
}
104-
105-
// Note any variables used by the pattern
106-
for (varName, _) in self.pattern.variableUses {
107-
if varName.characters.first == "@" {
108-
// If we failed with a builtin variable like @LINE, try to report
109-
// what it is bound to.
110-
if let value = self.pattern.evaluateExpression(varName) {
111-
diagnose(.note,
112-
at: self.loc,
113-
with: "with expression '\(varName)' equal to '\(value)'",
114-
options: options
115-
)
116-
} else {
117-
// If evaluation fails, we must have an incorrect builtin variable.
118-
diagnose(.note,
119-
at: self.loc,
120-
with: "uses incorrect expression '\(varName)'",
121-
options: options
122-
)
123-
}
124-
} else {
125-
if let varDef = self.pattern.variableDefs[varName] {
126-
diagnose(.note,
127-
at: self.loc,
128-
with: "with variable '\(varName)' equal to '\(varDef)'",
129-
options: options
130-
)
131-
} else {
132-
diagnose(.note,
133-
at: self.loc,
134-
with: "uses undefined variable '\(varName)'",
135-
options: options
136-
)
137-
}
138-
}
139-
}
140-
141-
var NumLinesForward = 0
142-
var BestLine : Int? = nil
143-
var BestQuality = 0.0
144-
145-
for i in 0..<min(buffer.characters.count, 4096) {
146-
let exampleString : String
147-
if pattern.fixedString.isEmpty {
148-
exampleString = pattern.regExPattern
149-
} else {
150-
exampleString = pattern.fixedString
151-
}
152-
153-
if exampleString.isEmpty {
154-
break
155-
}
156-
157-
let char = buffer[buffer.index(buffer.startIndex, offsetBy: i)]
158-
if char == "\n" {
159-
NumLinesForward += 1
160-
}
161-
162-
// Patterns have leading whitespace stripped, so skip whitespace when
163-
// looking for something which looks like a pattern.
164-
if char == " " || char == "\t" {
165-
continue;
166-
}
167-
168-
// Compute the "quality" of this match as an arbitrary combination of
169-
// the match distance and the number of lines skipped to get to this
170-
// match.
171-
let distance = editDistance(from: Array(buffer.characters), to: Array(exampleString.characters))
172-
let quality = Double(distance) + (Double(NumLinesForward) / 100.0)
173-
if quality < BestQuality || BestLine == nil {
174-
BestLine = i
175-
BestQuality = quality
176-
}
177-
}
178-
179-
if let Best = BestLine, BestQuality < 50 {
180-
buffer.utf8CString.withUnsafeBufferPointer { buf in
181-
let otherPatternLoc = CheckLoc.inBuffer(
182-
buf.baseAddress!.advanced(by: Best),
183-
UnsafeBufferPointer(start: buf.baseAddress?.advanced(by: Best), count: buf.count - Best)
184-
)
185-
diagnose(.note, at: otherPatternLoc, with: "possible intended match here", options: options)
186-
}
187-
}
188-
83+
diagnoseFailedCheck(variableTable, options, buffer)
18984
return nil
19085
}
19186
let (matchPos, matchLen) = (range.location, range.length)
@@ -405,4 +300,112 @@ struct CheckString {
405300

406301
return (lastPos, notStrings, finalTable)
407302
}
303+
304+
private func diagnoseFailedCheck(_ variableTable: [String : String], _ options: FileCheckOptions, _ buffer: String) {
305+
if let rtm = self.pattern.computeRegexToMatch(variableTable) {
306+
if !self.pattern.fixedString.isEmpty {
307+
diagnose(.error,
308+
at: self.loc,
309+
with: self.prefix + ": could not find '\(self.pattern.fixedString)' (with regex '\(rtm)') in input",
310+
options: options
311+
)
312+
} else {
313+
diagnose(.error,
314+
at: self.loc,
315+
with: self.prefix + ": could not find a match for regex '\(rtm)' in input",
316+
options: options
317+
)
318+
}
319+
} else {
320+
diagnose(.error,
321+
at: self.loc,
322+
with: self.prefix + ": could not find '\(self.pattern.fixedString)' in input",
323+
options: options
324+
)
325+
}
326+
327+
// Note any variables used by the pattern
328+
for (varName, _) in self.pattern.variableUses {
329+
if varName.characters.first == "@" {
330+
// If we failed with a builtin variable like @LINE, try to report
331+
// what it is bound to.
332+
if let value = self.pattern.evaluateExpression(varName) {
333+
diagnose(.note,
334+
at: self.loc,
335+
with: "with expression '\(varName)' equal to '\(value)'",
336+
options: options
337+
)
338+
} else {
339+
// If evaluation fails, we must have an incorrect builtin variable.
340+
diagnose(.note,
341+
at: self.loc,
342+
with: "uses incorrect expression '\(varName)'",
343+
options: options
344+
)
345+
}
346+
} else {
347+
if let varDef = self.pattern.variableDefs[varName] {
348+
diagnose(.note,
349+
at: self.loc,
350+
with: "with variable '\(varName)' equal to '\(varDef)'",
351+
options: options
352+
)
353+
} else {
354+
diagnose(.note,
355+
at: self.loc,
356+
with: "uses undefined variable '\(varName)'",
357+
options: options
358+
)
359+
}
360+
}
361+
}
362+
363+
var NumLinesForward = 0
364+
var BestLine : Int? = nil
365+
var BestQuality = 0.0
366+
367+
for i in 0..<min(buffer.characters.count, 4096) {
368+
let exampleString : String
369+
if pattern.fixedString.isEmpty {
370+
exampleString = pattern.regExPattern
371+
} else {
372+
exampleString = pattern.fixedString
373+
}
374+
375+
if exampleString.isEmpty {
376+
break
377+
}
378+
379+
let char = buffer[buffer.index(buffer.startIndex, offsetBy: i)]
380+
if char == "\n" {
381+
NumLinesForward += 1
382+
}
383+
384+
// Patterns have leading whitespace stripped, so skip whitespace when
385+
// looking for something which looks like a pattern.
386+
if char == " " || char == "\t" {
387+
continue;
388+
}
389+
390+
// Compute the "quality" of this match as an arbitrary combination of
391+
// the match distance and the number of lines skipped to get to this
392+
// match.
393+
let distance = editDistance(from: Array(buffer.characters), to: Array(exampleString.characters))
394+
let quality = Double(distance) + (Double(NumLinesForward) / 100.0)
395+
if quality < BestQuality || BestLine == nil {
396+
BestLine = i
397+
BestQuality = quality
398+
}
399+
}
400+
401+
if let Best = BestLine, BestQuality < 50 {
402+
buffer.utf8CString.withUnsafeBufferPointer { buf in
403+
let otherPatternLoc = CheckLoc.inBuffer(
404+
buf.baseAddress!.advanced(by: Best),
405+
UnsafeBufferPointer(start: buf.baseAddress?.advanced(by: Best), count: buf.count - Best)
406+
)
407+
diagnose(.note, at: otherPatternLoc, with: "possible intended match here", options: options)
408+
}
409+
}
410+
}
408411
}

Sources/FileCheck/FileCheck.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ private func check(input b : String, against checkStrings : [CheckString], optio
468468
checkRegion = buffer
469469
} else {
470470
let checkStr = checkStrings[j]
471-
if checkStr.pattern.type != .label {
471+
guard checkStr.pattern.type == .label else {
472472
j += 1
473473
continue
474474
}

Tests/FileCheckTests/DAGSpec.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ class DAGSpec : XCTestCase {
1414
print("this is the string to be matched")
1515
})
1616
}
17-
17+
18+
#if swift(>=4)
1819
func testDAGWithInst() {
1920
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK-INSTDAG"]) {
2021
// CHECK-INSTDAG-DAG: add [[REG1:r[0-9]+]], r1, r2
@@ -143,4 +144,5 @@ class DAGSpec : XCTestCase {
143144
""")
144145
})
145146
}
147+
#endif
146148
}

Tests/FileCheckTests/EmptySpec.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@ import XCTest
33
import Foundation
44

55
class EmptySpec : XCTestCase {
6-
// func testEmptyError() {
7-
// XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["EMPTY-ERR"]) {
8-
// // EMPTY-ERR: FileCheck error: input from file descriptor stdout is empty.
9-
// XCTAssertFalse(fileCheckOutput(options: [.disableColors]) {})
10-
// })
11-
// }
6+
func testEmptyError() {
7+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["EMPTY-ERR"]) {
8+
// EMPTY-ERR: FileCheck error: input from file descriptor stdout is empty.
9+
XCTAssertFalse(fileCheckOutput(options: [.disableColors]) {})
10+
})
11+
}
1212

1313
func testAllowEmpty() {
1414
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["ALLOW-EMPTY-ERR"], options: [.allowEmptyInput]) {

Tests/FileCheckTests/LabelSpec.swift

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import FileCheck
2+
import XCTest
3+
import Foundation
4+
5+
#if swift(>=4)
6+
class LabelSpec : XCTestCase {
7+
func testLabels() {
8+
let output = {
9+
print("""
10+
label0:
11+
a
12+
b
13+
14+
label1:
15+
b
16+
c
17+
18+
label2:
19+
a
20+
c
21+
""")
22+
}
23+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECKOK"]) {
24+
// CHECKOK-LABEL: {{^}}label0:
25+
// CHECKOK: {{^}}a
26+
// CHECKOK: {{^}}b
27+
28+
// CHECKOK-LABEL: {{^}}label1:
29+
// CHECKOK: {{^}}b
30+
// CHECKOK: {{^}}c
31+
32+
// CHECKOK-LABEL: {{^}}label2:
33+
// CHECKOK: {{^}}a
34+
// CHECKOK: {{^}}c
35+
output()
36+
})
37+
38+
39+
}
40+
}
41+
#endif

0 commit comments

Comments
 (0)