Skip to content

Commit 8a2ecbb

Browse files
committed
Use edit distance to suggest candidate check lines
1 parent 0dd9d1d commit 8a2ecbb

File tree

2 files changed

+64
-1
lines changed

2 files changed

+64
-1
lines changed

Sources/FileCheck/CheckString.swift

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,55 @@ struct CheckString {
137137
}
138138
}
139139
}
140+
141+
var NumLinesForward = 0
142+
var BestLine : Int? = nil
143+
var BestQuality = 0.0
144+
145+
for i in 0..<min(buffer.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(rebasing: buf.suffix(from: Best))
184+
)
185+
diagnose(.note, at: otherPatternLoc, with: "possible intended match here", options: options)
186+
}
187+
}
188+
140189
return nil
141190
}
142191
let (matchPos, matchLen) = (range.location, range.length)

Tests/FileCheckTests/FileCheckSpec.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,12 +127,26 @@ class FileCheckSpec : XCTestCase {
127127
})
128128
}
129129

130+
func testNearestPattern() {
131+
XCTAssert(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK-NEAREST-PATTERN-MSG"]) {
132+
// CHECK-NEAREST-PATTERN-MSG: error: {{.*}}: could not find 'Once more into the beach' (with regex '') in input
133+
// CHECK-NEAREST-PATTERN-MSG-NEXT: // {{.*}}: Once more into the beach
134+
// CHECK-NEAREST-PATTERN-MSG-NEXT: note: possible intended match here
135+
// CHECK-NEAREST-PATTERN-MSG-NEXT: Once more into the breach
136+
XCTAssertFalse(fileCheckOutput(of: .stdout, withPrefixes: ["CHECK-NEAREST-PATTERN"], options: [.disableColors]) {
137+
// CHECK-NEAREST-PATTERN: Once more into the beach
138+
print("Once more into the breach")
139+
})
140+
})
141+
}
142+
130143
#if !os(macOS)
131144
static var allTests = testCase([
132145
("testWhitespace", testWhitespace),
133146
("testSame", testSame),
134147
("testImplicitCheckNot", testImplicitCheckNot),
135-
("testUndefinedVariablePattern", testUndefinedVariablePattern)
148+
("testUndefinedVariablePattern", testUndefinedVariablePattern),
149+
("testNearestPattern", testNearestPattern),
136150
])
137151
#endif
138152
}

0 commit comments

Comments
 (0)