Skip to content

Commit fbf03a1

Browse files
authored
Merge pull request #38 from spotify/ibdocumentlocation
Parse class IBMemberDocumentLocation
2 parents 266b3e0 + daa1d8e commit fbf03a1

File tree

7 files changed

+169
-11
lines changed

7 files changed

+169
-11
lines changed

Sources/XCLogParser/activityparser/ActivityParser.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,6 @@ public class ActivityParser {
223223
edges: try parseStepEdges(iterator: &iterator))
224224
}
225225

226-
//
227-
228226
public func parseIDEActivityLogAnalyzerControlFlowStepEdge(iterator: inout IndexingIterator<[Token]>) throws
229227
-> IDEActivityLogAnalyzerControlFlowStepEdge {
230228
return IDEActivityLogAnalyzerControlFlowStepEdge(
@@ -270,7 +268,7 @@ public class ActivityParser {
270268
}
271269
}
272270

273-
private func parseDocumentLocation(iterator: inout IndexingIterator<[Token]>) throws -> DVTDocumentLocation {
271+
public func parseDocumentLocation(iterator: inout IndexingIterator<[Token]>) throws -> DVTDocumentLocation {
274272
let classRefToken = try getClassRefToken(iterator: &iterator)
275273
if case Token.null = classRefToken {
276274
return DVTDocumentLocation(documentURLString: "", timestamp: 0.0)
@@ -282,6 +280,8 @@ public class ActivityParser {
282280
return try parseDVTTextDocumentLocation(iterator: &iterator)
283281
} else if className == String(describing: DVTDocumentLocation.self) {
284282
return try parseDVTDocumentLocation(iterator: &iterator)
283+
} else if className == String(describing: IBDocumentMemberLocation.self) {
284+
return try parseIBDocumentMemberLocation(iterator: &iterator)
285285
}
286286
throw XCLogParserError.parseError("Unexpected className found parsing DocumentLocation \(className)")
287287
}
@@ -454,6 +454,41 @@ public class ActivityParser {
454454
}
455455
}
456456

457+
private func parseIBDocumentMemberLocation(iterator: inout IndexingIterator<[Token]>)
458+
throws -> IBDocumentMemberLocation {
459+
return IBDocumentMemberLocation(documentURLString: try parseAsString(token: iterator.next()),
460+
timestamp: try parseAsDouble(token: iterator.next()),
461+
memberIdentifier: try parseIBMemberID(iterator: &iterator),
462+
attributeSearchLocation:
463+
try parseIBAttributeSearchLocation(iterator: &iterator))
464+
}
465+
466+
private func parseIBMemberID(iterator: inout IndexingIterator<[Token]>)
467+
throws -> IBMemberID {
468+
let classRefToken = try getClassRefToken(iterator: &iterator)
469+
guard case Token.classNameRef(let className) = classRefToken else {
470+
throw XCLogParserError.parseError("Unexpected token found parsing " +
471+
"IBMemberID \(classRefToken)")
472+
}
473+
474+
if className == String(describing: IBMemberID.self) {
475+
return IBMemberID(memberIdentifier: try parseAsString(token: iterator.next()))
476+
}
477+
throw XCLogParserError.parseError("Unexpected className found parsing " +
478+
"IBMemberID \(className)")
479+
}
480+
481+
private func parseIBAttributeSearchLocation(iterator: inout IndexingIterator<[Token]>)
482+
throws -> IBAttributeSearchLocation? {
483+
guard let nextToken = iterator.next() else {
484+
throw XCLogParserError.parseError("Unexpected EOF parsing IBAttributeSearchLocation")
485+
}
486+
if case Token.null = nextToken {
487+
return nil
488+
}
489+
throw XCLogParserError.parseError("Unexpected Token parsing IBAttributeSearchLocation: \(nextToken)")
490+
}
491+
457492
private func parseAsString(token: Token?) throws -> String {
458493
guard let token = token else {
459494
throw XCLogParserError.parseError("Unexpected EOF parsing String")

Sources/XCLogParser/activityparser/IDEActivityModel.swift

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -457,8 +457,8 @@ public class DBGConsoleLog: IDEActivityLogSection {
457457
}
458458

459459
public class IDEActivityLogAnalyzerControlFlowStepEdge: Encodable {
460-
let startLocation: DVTDocumentLocation
461-
let endLocation: DVTDocumentLocation
460+
public let startLocation: DVTDocumentLocation
461+
public let endLocation: DVTDocumentLocation
462462

463463
public init(startLocation: DVTDocumentLocation, endLocation: DVTDocumentLocation) {
464464
self.startLocation = startLocation
@@ -468,9 +468,9 @@ public class IDEActivityLogAnalyzerControlFlowStepEdge: Encodable {
468468

469469
public class IDEActivityLogAnalyzerEventStepMessage: IDEActivityLogMessage {
470470

471-
let parentIndex: UInt64
472-
let description: String
473-
let callDepth: UInt64
471+
public let parentIndex: UInt64
472+
public let description: String
473+
public let callDepth: UInt64
474474

475475
public init(title: String,
476476
shortTitle: String,
@@ -520,3 +520,51 @@ public class IDEActivityLogAnalyzerEventStepMessage: IDEActivityLogMessage {
520520
try container.encode(callDepth, forKey: .callDepth)
521521
}
522522
}
523+
524+
// MARK: IDEInterfaceBuilderKit
525+
526+
public class IBMemberID: Encodable {
527+
public let memberIdentifier: String
528+
529+
public init(memberIdentifier: String) {
530+
self.memberIdentifier = memberIdentifier
531+
}
532+
}
533+
534+
public class IBAttributeSearchLocation: Encodable {
535+
public let offsetFromStart: UInt64
536+
public let offsetFromEnd: UInt64
537+
public let keyPath: String
538+
539+
public init(offsetFromStart: UInt64, offsetFromEnd: UInt64, keyPath: String) {
540+
self.offsetFromEnd = offsetFromEnd
541+
self.offsetFromStart = offsetFromStart
542+
self.keyPath = keyPath
543+
}
544+
}
545+
546+
public class IBDocumentMemberLocation: DVTDocumentLocation {
547+
public let memberIdentifier: IBMemberID
548+
public let attributeSearchLocation: IBAttributeSearchLocation?
549+
550+
public init(documentURLString: String,
551+
timestamp: Double,
552+
memberIdentifier: IBMemberID,
553+
attributeSearchLocation: IBAttributeSearchLocation?) {
554+
self.memberIdentifier = memberIdentifier
555+
self.attributeSearchLocation = attributeSearchLocation
556+
super.init(documentURLString: documentURLString, timestamp: timestamp)
557+
}
558+
559+
private enum CodingKeys: String, CodingKey {
560+
case memberIdentifier
561+
case attributeSearchLocation
562+
}
563+
564+
override public func encode(to encoder: Encoder) throws {
565+
try super.encode(to: encoder)
566+
var container = encoder.container(keyedBy: CodingKeys.self)
567+
try container.encode(memberIdentifier, forKey: .memberIdentifier)
568+
try container.encode(attributeSearchLocation, forKey: .attributeSearchLocation)
569+
}
570+
}

Sources/XCLogParser/commands/Version.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,6 @@ import Foundation
2121

2222
public struct Version {
2323

24-
public static let current = "0.1.6"
24+
public static let current = "0.1.7"
2525

2626
}

Sources/XCLogParser/extensions/ArrayExtension.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ extension Array where Element: Notice {
3737
$0.type == .swiftWarning ||
3838
$0.type == .clangWarning ||
3939
$0.type == .projectWarning ||
40-
$0.type == .analyzerWarning
40+
$0.type == .analyzerWarning ||
41+
$0.type == .interfaceBuilderWarning
4142
}
4243
}
4344

Sources/XCLogParser/parser/Notice.swift

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ public enum NoticeType: String, Encodable {
4747
/// A warning returned by Xcode static analyzer
4848
case analyzerWarning
4949

50+
/// A warning inside an Interface Builder file
51+
case interfaceBuilderWarning
52+
5053
public static func fromTitle(_ title: String) -> NoticeType? {
5154
switch title {
5255
case "Swift Compiler Warning":
@@ -65,6 +68,8 @@ public enum NoticeType: String, Encodable {
6568
return .error
6669
case Suffix("Notice"):
6770
return .note
71+
case Prefix("/* com.apple.ibtool.document.warnings */"):
72+
return .interfaceBuilderWarning
6873
default:
6974
return .note
7075
}
@@ -86,6 +91,7 @@ public class Notice: Encodable {
8691
public let endingColumnNumber: UInt64
8792
public let characterRangeEnd: UInt64
8893
public let characterRangeStart: UInt64
94+
public let interfaceBuilderIdentifier: String?
8995

9096
static var clangWarningRegexp: NSRegularExpression? = {
9197
let pattern = "\\[(-W[\\w-]*)\\]+"
@@ -98,6 +104,11 @@ public class Notice: Encodable {
98104
guard let type = type else {
99105
return nil
100106
}
107+
if let location = logMessage.location as? IBDocumentMemberLocation {
108+
self.interfaceBuilderIdentifier = location.memberIdentifier.memberIdentifier
109+
} else {
110+
self.interfaceBuilderIdentifier = nil
111+
}
101112
if let location = logMessage.location as? DVTTextDocumentLocation {
102113
self.type = type
103114
if let analyzerMessage = logMessage as? IDEActivityLogAnalyzerEventStepMessage {
@@ -158,7 +169,10 @@ public class Notice: Encodable {
158169
return nil
159170
}
160171
}
161-
if let notice = Notice(withType: NoticeType.fromTitle(message.categoryIdent), logMessage: message) {
172+
// Special case, Interface builder warning can only be spotted by checking the whole text of the
173+
// log section
174+
let noticeTypeTitle = message.categoryIdent.isEmpty ? logSection.text : message.categoryIdent
175+
if let notice = Notice(withType: NoticeType.fromTitle(noticeTypeTitle), logMessage: message) {
162176
return [notice]
163177
}
164178
return nil

Tests/XCLogParserTests/ActivityParserTests.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,17 @@ class ActivityParserTests: XCTestCase {
207207
Token.int(0),
208208
Token.null]
209209

210+
let IBDocumentMemberLocationTokens: [Token] = [
211+
Token.className("IBDocumentMemberLocation"),
212+
Token.classNameRef("IBDocumentMemberLocation"),
213+
Token.string("file:///projects/WarningTest/WarningTest/Base.lproj/Main.storyboard"),
214+
Token.double(0.0),
215+
Token.className("IBMemberID"),
216+
Token.classNameRef("IBMemberID"),
217+
Token.string("RgO-vd-uiQ"),
218+
Token.null
219+
]
220+
210221
func testParseDVTTextDocumentLocation() throws {
211222
let tokens = textDocumentLocationTokens
212223
var iterator = tokens.makeIterator()
@@ -299,4 +310,18 @@ class ActivityParserTests: XCTestCase {
299310
}
300311
}
301312

313+
func testParseIBDocumentMemberLocation() throws {
314+
var iterator = IBDocumentMemberLocationTokens.makeIterator()
315+
let documentLocation = try parser.parseDocumentLocation(iterator: &iterator)
316+
XCTAssert(documentLocation is IBDocumentMemberLocation,
317+
"Document location should be a IBDocumentMemberLocation")
318+
XCTAssertEqual("file:///projects/WarningTest/WarningTest/Base.lproj/Main.storyboard",
319+
documentLocation.documentURLString, "The url should be correctly parsed")
320+
guard let documentMemberLocation = documentLocation as? IBDocumentMemberLocation else {
321+
return
322+
}
323+
XCTAssertEqual("RgO-vd-uiQ",
324+
documentMemberLocation.memberIdentifier.memberIdentifier,
325+
"IBMember's identifier should be parsed")
326+
}
302327
}

Tests/XCLogParserTests/ParserTests.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,41 @@ class ParserTests: XCTestCase {
128128
}
129129
XCTAssertEqual(warningMessage.title, warning.title)
130130
XCTAssertEqual("[-Wdeprecated-declarations]", warning.clangFlag ?? "empty")
131+
XCTAssertNil(warning.interfaceBuilderIdentifier)
132+
}
133+
134+
func testParseInterfaceBuilderWarning() throws {
135+
let timestamp = Date().timeIntervalSinceReferenceDate
136+
let memberId = IBMemberID(memberIdentifier: "ABC")
137+
let ibDocumentLocation = IBDocumentMemberLocation(
138+
documentURLString: "file://project/Base.lproj/Main.storyboard",
139+
timestamp: timestamp,
140+
memberIdentifier: memberId,
141+
attributeSearchLocation: nil)
142+
let warningMessage = IDEActivityLogMessage(title: "Automatically Adjusts Font requires using a Dynamic Type",
143+
shortTitle: "",
144+
timeEmitted: timestamp,
145+
rangeEndInSectionText: 0,
146+
rangeStartInSectionText: 0,
147+
subMessages: [],
148+
severity: 1,
149+
type: "",
150+
location: ibDocumentLocation,
151+
categoryIdent: "",
152+
secondaryLocations: [],
153+
additionalDescription: "")
154+
let fakeLog = getFakeIDEActivityLogWithMessage(warningMessage,
155+
andText: "/* com.apple.ibtool.document.warnings */ " +
156+
"/project/Base.lproj/Main.storyboard:ABC: warning:")
157+
let build = try parser.parse(activityLog: fakeLog)
158+
guard let warning = build.warnings?.first else {
159+
XCTFail("Build's warnings are empty")
160+
return
161+
}
162+
XCTAssertEqual(NoticeType.interfaceBuilderWarning, warning.type)
163+
XCTAssertEqual(warningMessage.title, warning.title)
164+
XCTAssertNil(warning.clangFlag)
165+
XCTAssertEqual(memberId.memberIdentifier, warning.interfaceBuilderIdentifier)
131166
}
132167

133168
private func getFakeIDEActivityLogWithMessage(_ message: IDEActivityLogMessage,

0 commit comments

Comments
 (0)