Skip to content

Commit 72f9740

Browse files
committed
Split generation of missing token errors to separate file
1 parent 2854249 commit 72f9740

File tree

3 files changed

+121
-91
lines changed

3 files changed

+121
-91
lines changed

Sources/SwiftParserDiagnostics/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_swift_host_library(SwiftParserDiagnostics
1010
DiagnosticExtensions.swift
1111
LexerDiagnosticMessages.swift
1212
MissingNodesError.swift
13+
MissingTokenError.swift
1314
ParserDiagnosticMessages.swift
1415
ParseDiagnosticsGenerator.swift
1516
PresenceUtils.swift

Sources/SwiftParserDiagnostics/MissingNodesError.swift

Lines changed: 0 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -293,97 +293,6 @@ public struct InsertTokenFixIt: ParserFixIt {
293293
// MARK: - Generate Error
294294

295295
extension ParseDiagnosticsGenerator {
296-
func handleMissingToken(_ node: TokenSyntax) {
297-
guard let invalidToken = node.previousToken(viewMode: .all),
298-
let unexpectedTokens = invalidToken.parent?.as(UnexpectedNodesSyntax.self),
299-
unexpectedTokens.count == 1
300-
else {
301-
_ = handleMissingSyntax(node)
302-
return
303-
}
304-
305-
// The previous token is unexpected, assume that it was intended to be
306-
// this token.
307-
308-
if node.tokenKind.isIdentifier {
309-
let fixIts: [FixIt]
310-
if invalidToken.tokenKind.isLexerClassifiedKeyword || invalidToken.tokenKind.isDollarIdentifier {
311-
// TODO: Should the parser add the text with backticks to the missing
312-
// node? Then this could just make missing/present.
313-
fixIts = [
314-
FixIt(
315-
message: .wrapInBackticks,
316-
changes: [
317-
.replace(
318-
oldNode: Syntax(invalidToken),
319-
newNode: Syntax(TokenSyntax.identifier("`\(invalidToken.text)`", leadingTrivia: invalidToken.leadingTrivia, trailingTrivia: invalidToken.trailingTrivia))
320-
)
321-
]
322-
)
323-
]
324-
} else {
325-
fixIts = []
326-
}
327-
addDiagnostic(
328-
invalidToken,
329-
InvalidIdentifierError(invalidIdentifier: invalidToken, missingIdentifier: node),
330-
fixIts: fixIts,
331-
handledNodes: [unexpectedTokens.id]
332-
)
333-
} else if node.tokenKind == .period && invalidToken.tokenKind == .period {
334-
// Trailing trivia is the cause of this diagnostic, don't transfer it.
335-
let changes: [FixIt.Changes] = [
336-
.makeMissing(invalidToken, transferTrivia: false),
337-
.makePresent(node),
338-
]
339-
340-
if let identifier = node.nextToken(viewMode: .all),
341-
identifier.rawTokenKind == .identifier,
342-
identifier.presence == .missing
343-
{
344-
// The extraneous whitespace caused a missing identifier, output a
345-
// diagnostic inserting it instead of a diagnostic to fix the trivia
346-
// around the period.
347-
_ = handleMissingSyntax(
348-
identifier,
349-
overridePosition: invalidToken.endPositionBeforeTrailingTrivia,
350-
additionalChanges: changes,
351-
additionalHandledNodes: [unexpectedTokens.id]
352-
)
353-
} else {
354-
let fixIt = FixIt(message: .removeExtraneousWhitespace, changes: changes)
355-
addDiagnostic(invalidToken, .invalidWhitespaceAfterPeriod, fixIts: [fixIt], handledNodes: [unexpectedTokens.id])
356-
}
357-
} else if node.rawTokenKind == .rawStringDelimiter, invalidToken.rawTokenKind == .rawStringDelimiter {
358-
let message: DiagnosticMessage
359-
if node.parent?.is(ExpressionSegmentSyntax.self) == true {
360-
message = .tooManyRawStringDelimitersToStartInterpolation
361-
} else {
362-
assert(
363-
node.parent?.is(StringLiteralExprSyntax.self) == true,
364-
"Raw string delimiters should only occur in string interpolation and at the end of a string literal"
365-
)
366-
message = .tooManyClosingRawStringDelimiters
367-
}
368-
let fixIt = FixIt(
369-
message: .removeExtraneousDelimiters,
370-
changes: [
371-
.makeMissing(invalidToken),
372-
.makePresentBeforeTrivia(node),
373-
]
374-
)
375-
addDiagnostic(
376-
invalidToken,
377-
position: invalidToken.positionAfterSkippingLeadingTrivia.advanced(by: node.contentLength.utf8Length),
378-
message,
379-
fixIts: [fixIt],
380-
handledNodes: [unexpectedTokens.id]
381-
)
382-
} else {
383-
_ = handleMissingSyntax(node)
384-
}
385-
}
386-
387296
func handleMissingSyntax<T: SyntaxProtocol>(
388297
_ node: T,
389298
overridePosition: AbsolutePosition? = nil,
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftDiagnostics
14+
@_spi(RawSyntax) import SwiftSyntax
15+
16+
extension ParseDiagnosticsGenerator {
17+
func handleMissingToken(_ missingToken: TokenSyntax) {
18+
guard let invalidToken = missingToken.previousToken(viewMode: .all),
19+
let invalidTokenContainer = invalidToken.parent?.as(UnexpectedNodesSyntax.self),
20+
invalidTokenContainer.count == 1
21+
else {
22+
_ = handleMissingSyntax(missingToken)
23+
return
24+
}
25+
26+
// The previous token is unexpected, assume that it was intended to be
27+
// this token.
28+
29+
switch (missingToken.rawTokenKind, invalidToken.rawTokenKind) {
30+
case (.identifier, _):
31+
handleInvalidIdentifier(invalidToken: invalidToken, missingToken: missingToken, invalidTokenContainer: invalidTokenContainer)
32+
case (.period, .period):
33+
handleInvalidPeriod(invalidToken: invalidToken, missingToken: missingToken, invalidTokenContainer: invalidTokenContainer)
34+
case (.rawStringDelimiter, .rawStringDelimiter):
35+
handleInvalidRawStringDelimiter(invalidToken: invalidToken, missingToken: missingToken, invalidTokenContainer: invalidTokenContainer)
36+
default:
37+
_ = handleMissingSyntax(missingToken)
38+
}
39+
}
40+
41+
private func handleInvalidIdentifier(invalidToken: TokenSyntax, missingToken: TokenSyntax, invalidTokenContainer: UnexpectedNodesSyntax) {
42+
let fixIts: [FixIt]
43+
if invalidToken.tokenKind.isLexerClassifiedKeyword || invalidToken.tokenKind.isDollarIdentifier {
44+
// TODO: Should the parser add the text with backticks to the missing
45+
// node? Then this could just make missing/present.
46+
fixIts = [
47+
FixIt(
48+
message: .wrapInBackticks,
49+
changes: [
50+
.replace(
51+
oldNode: Syntax(invalidToken),
52+
newNode: Syntax(TokenSyntax.identifier("`\(invalidToken.text)`", leadingTrivia: invalidToken.leadingTrivia, trailingTrivia: invalidToken.trailingTrivia))
53+
)
54+
]
55+
)
56+
]
57+
} else {
58+
fixIts = []
59+
}
60+
addDiagnostic(
61+
invalidToken,
62+
InvalidIdentifierError(invalidIdentifier: invalidToken, missingIdentifier: missingToken),
63+
fixIts: fixIts,
64+
handledNodes: [invalidTokenContainer.id]
65+
)
66+
}
67+
68+
private func handleInvalidPeriod(invalidToken: TokenSyntax, missingToken: TokenSyntax, invalidTokenContainer: UnexpectedNodesSyntax) {
69+
// Trailing trivia is the cause of this diagnostic, don't transfer it.
70+
let changes: [FixIt.Changes] = [
71+
.makeMissing(invalidToken, transferTrivia: false),
72+
.makePresent(missingToken),
73+
]
74+
75+
if let identifier = missingToken.nextToken(viewMode: .all),
76+
identifier.rawTokenKind == .identifier,
77+
identifier.presence == .missing
78+
{
79+
// The extraneous whitespace caused a missing identifier, output a
80+
// diagnostic inserting it instead of a diagnostic to fix the trivia
81+
// around the period.
82+
_ = handleMissingSyntax(
83+
identifier,
84+
overridePosition: invalidToken.endPositionBeforeTrailingTrivia,
85+
additionalChanges: changes,
86+
additionalHandledNodes: [invalidTokenContainer.id]
87+
)
88+
} else {
89+
let fixIt = FixIt(message: .removeExtraneousWhitespace, changes: changes)
90+
addDiagnostic(invalidToken, .invalidWhitespaceAfterPeriod, fixIts: [fixIt], handledNodes: [invalidTokenContainer.id])
91+
}
92+
}
93+
94+
private func handleInvalidRawStringDelimiter(invalidToken: TokenSyntax, missingToken: TokenSyntax, invalidTokenContainer: UnexpectedNodesSyntax) {
95+
let message: DiagnosticMessage
96+
if missingToken.parent?.is(ExpressionSegmentSyntax.self) == true {
97+
message = .tooManyRawStringDelimitersToStartInterpolation
98+
} else {
99+
assert(
100+
missingToken.parent?.is(StringLiteralExprSyntax.self) == true,
101+
"Raw string delimiters should only occur in string interpolation and at the end of a string literal"
102+
)
103+
message = .tooManyClosingRawStringDelimiters
104+
}
105+
let fixIt = FixIt(
106+
message: .removeExtraneousDelimiters,
107+
changes: [
108+
.makeMissing(invalidToken),
109+
.makePresentBeforeTrivia(missingToken),
110+
]
111+
)
112+
addDiagnostic(
113+
invalidToken,
114+
position: invalidToken.positionAfterSkippingLeadingTrivia.advanced(by: missingToken.contentLength.utf8Length),
115+
message,
116+
fixIts: [fixIt],
117+
handledNodes: [invalidTokenContainer.id]
118+
)
119+
}
120+
}

0 commit comments

Comments
 (0)