Skip to content

Commit 99152e4

Browse files
committed
Parse shebang as a child of SourceFileSyntax
1 parent e340af4 commit 99152e4

27 files changed

+164
-104
lines changed

CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,12 @@ public let DECL_NODES: [Node] = [
19971997
parserFunction: "parseSourceFile",
19981998
traits: ["WithStatements"],
19991999
children: [
2000+
Child(
2001+
name: "Shebang",
2002+
kind: .token(choices: [.token(tokenKind: "ShebangToken")]),
2003+
documentation: "A shebang can specify the path of the compiler when using Swift source file as a script.",
2004+
isOptional: true
2005+
),
20002006
Child(
20012007
name: "Statements",
20022008
kind: .collection(kind: .codeBlockItemList, collectionElementName: "Statement")

CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ public let SYNTAX_TOKENS: [TokenSpec] = [
8080
.punctuator(name: "equal", text: "="),
8181
.punctuator(name: "exclamationMark", text: "!"),
8282
.other(name: "floatLiteral", nameForDiagnostics: "float literal"),
83+
.other(name: "shebang", nameForDiagnostics: "shebang"),
8384
.other(name: "identifier", nameForDiagnostics: "identifier"),
8485
.punctuator(name: "infixQuestionMark", text: "?"),
8586
.other(name: "integerLiteral", nameForDiagnostics: "integer literal"),

CodeGeneration/Sources/SyntaxSupport/Trivia.swift

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -159,11 +159,6 @@ public let TRIVIAS: [Trivia] = [
159159
]
160160
),
161161

162-
Trivia(
163-
name: "Shebang",
164-
comment: #"A script command, starting with '#!'."#
165-
),
166-
167162
Trivia(
168163
name: "Space",
169164
comment: #"A space ' ' character."#,

Sources/SwiftIDEUtils/SyntaxClassification.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ extension RawTokenKind {
193193
return .none
194194
case .semicolon:
195195
return .none
196+
case .shebang:
197+
return .none
196198
case .singleQuote:
197199
return .stringLiteral
198200
case .stringQuote:

Sources/SwiftParser/Lexer/Cursor.swift

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,11 @@ extension Lexer.Cursor {
905905
case UInt8(ascii: "\\"): _ = self.advance(); return Lexer.Result(.backslash)
906906

907907
case UInt8(ascii: "#"):
908+
// Try lex shebang.
909+
if self.isAtStartOfFile, self.peek(at: 1) == UInt8(ascii: "!") {
910+
self.advanceToEndOfLine()
911+
return Lexer.Result(.shebang)
912+
}
908913
// Try lex a raw string literal.
909914
if let delimiterLength = self.advanceIfOpeningRawStringDelimiter() {
910915
return Lexer.Result(.rawStringPoundDelimiter, stateTransition: .push(newState: .afterRawStringDelimiter(delimiterLength: delimiterLength)))
@@ -1172,11 +1177,7 @@ extension Lexer.Cursor {
11721177
break
11731178
}
11741179
case UInt8(ascii: "#"):
1175-
guard start.isAtStartOfFile, self.advance(if: { $0 == "!" }) else {
1176-
break
1177-
}
1178-
self.advanceToEndOfLine()
1179-
continue
1180+
break
11801181
case UInt8(ascii: "<"), UInt8(ascii: ">"):
11811182
if self.tryLexConflictMarker(start: start) {
11821183
error = LexingDiagnostic(.sourceConflictMarker, position: start)

Sources/SwiftParser/Lexer/RegexLiteralLexer.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -625,6 +625,10 @@ extension Lexer.Cursor {
625625
case .arrow, .ellipsis, .period, .atSign, .pound, .backtick, .backslash:
626626
return false
627627

628+
// Shebang does not sequence expressions.
629+
case .shebang:
630+
return false
631+
628632
case .keyword:
629633
// There are a handful of keywords that are expressions, handle them.
630634
// Otherwise, a regex literal can generally be parsed after a keyword.

Sources/SwiftParser/TokenPrecedence.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,8 @@ enum TokenPrecedence: Comparable {
124124
.dollarIdentifier, .identifier,
125125
// '_' can occur in types to replace a type identifier
126126
.wildcard,
127-
// String segment, string interpolation anchor, pound, and regex pattern don't really fit anywhere else
128-
.pound, .stringSegment, .regexLiteralPattern:
127+
// String segment, string interpolation anchor, pound, shebang and regex pattern don't really fit anywhere else
128+
.pound, .stringSegment, .regexLiteralPattern, .shebang:
129129
self = .identifierLike
130130

131131
// MARK: Expr keyword

Sources/SwiftParser/TopLevel.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,12 @@ extension Parser {
4040
/// ``Parser/parse(source:parseTransition:filenameForDiagnostics:languageVersion:enableBareSlashRegexLiteral:)-7tndx``
4141
/// API calls.
4242
mutating func parseSourceFile() -> RawSourceFileSyntax {
43+
let shebang = self.consume(if: .shebang)
4344
let items = self.parseTopLevelCodeBlockItems()
4445
let unexpectedBeforeEndOfFileToken = consumeRemainingTokens()
4546
let endOfFile = self.consume(if: .endOfFile)!
4647
return .init(
48+
shebang: shebang,
4749
statements: items,
4850
RawUnexpectedNodesSyntax(unexpectedBeforeEndOfFileToken, arena: self.arena),
4951
endOfFileToken: endOfFile,

Sources/SwiftParser/TriviaParser.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,15 +81,6 @@ public struct TriviaParser {
8181
}
8282

8383
case UInt8(ascii: "#"):
84-
// "#!...": .shebang
85-
// NOTE: .shebang appears only if this trivia is at the start of the
86-
// file. We don't know if this trivia is at the start of the file, but
87-
// we believe that the lexer lexed it accordingly.
88-
if position == .leading && pieces.isEmpty && cursor.advance(if: { $0 == "!" }) {
89-
cursor.advanceToEndOfLine()
90-
pieces.append(.shebang(start.text(upTo: cursor)))
91-
continue
92-
}
9384
cursor.advance(while: { $0 == "#" })
9485
pieces.append(.pounds(start.distance(to: cursor)))
9586
continue

Sources/SwiftParser/generated/TokenSpecStaticMembers.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ extension TokenSpec {
6767
return TokenSpec(.floatLiteral)
6868
}
6969

70+
static var shebang: TokenSpec {
71+
return TokenSpec(.shebang)
72+
}
73+
7074
static var identifier: TokenSpec {
7175
return TokenSpec(.identifier)
7276
}

Sources/SwiftParserDiagnostics/generated/TokenNameForDiagnostics.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ extension TokenKind {
4343
return "!"
4444
case .floatLiteral:
4545
return "float literal"
46+
case .shebang:
47+
return "shebang"
4648
case .identifier:
4749
return "identifier"
4850
case .infixQuestionMark:

Sources/SwiftSyntax/SourceLocation.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,7 @@ fileprivate extension RawTriviaPiece {
616616
body(carriageReturnLineLength)
617617
}
618618
lineLength = .zero
619-
case let .shebang(text),
620-
let .lineComment(text),
619+
case let .lineComment(text),
621620
let .docLineComment(text):
622621
// Line comments are not supposed to contain newlines.
623622
precondition(!text.containsSwiftNewline(), "line comment created that contained a new-line character")

Sources/SwiftSyntax/Trivia.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ extension Trivia {
192192
return Array(repeating: TriviaPiece.carriageReturns(1), count: count)
193193
case .carriageReturnLineFeeds(let count):
194194
return Array(repeating: TriviaPiece.carriageReturnLineFeeds(1), count: count)
195-
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText, .shebang:
195+
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText:
196196
return [piece]
197197
}
198198
})

Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2779,8 +2779,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? {
27792779
return "constraint"
27802780
case \SomeOrAnyTypeSyntax.unexpectedAfterConstraint:
27812781
return "unexpectedAfterConstraint"
2782-
case \SourceFileSyntax.unexpectedBeforeStatements:
2783-
return "unexpectedBeforeStatements"
2782+
case \SourceFileSyntax.unexpectedBeforeShebang:
2783+
return "unexpectedBeforeShebang"
2784+
case \SourceFileSyntax.shebang:
2785+
return "shebang"
2786+
case \SourceFileSyntax.unexpectedBetweenShebangAndStatements:
2787+
return "unexpectedBetweenShebangAndStatements"
27842788
case \SourceFileSyntax.statements:
27852789
return "statements"
27862790
case \SourceFileSyntax.unexpectedBetweenStatementsAndEndOfFileToken:

Sources/SwiftSyntax/generated/RenamedChildrenCompatibility.swift

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7056,11 +7056,13 @@ extension SourceFileSyntax {
70567056
}
70577057
}
70587058

7059-
@available(*, deprecated, renamed: "SourceFileSyntax(leadingTrivia:_:statements:_:endOfFileToken:_:trailingTrivia:)")
7059+
@available(*, deprecated, renamed: "SourceFileSyntax(leadingTrivia:_:shebang:_:statements:_:endOfFileToken:_:trailingTrivia:)")
70607060
@_disfavoredOverload
70617061
public init(
70627062
leadingTrivia: Trivia? = nil,
7063-
_ unexpectedBeforeStatements: UnexpectedNodesSyntax? = nil,
7063+
_ unexpectedBeforeShebang: UnexpectedNodesSyntax? = nil,
7064+
shebang: TokenSyntax? = nil,
7065+
_ unexpectedBetweenShebangAndStatements: UnexpectedNodesSyntax? = nil,
70647066
statements: CodeBlockItemListSyntax,
70657067
_ unexpectedBetweenStatementsAndEOFToken: UnexpectedNodesSyntax? = nil,
70667068
eofToken: TokenSyntax = .endOfFileToken(),
@@ -7070,7 +7072,9 @@ extension SourceFileSyntax {
70707072
) {
70717073
self.init(
70727074
leadingTrivia: leadingTrivia,
7073-
unexpectedBeforeStatements,
7075+
unexpectedBeforeShebang,
7076+
shebang: shebang,
7077+
unexpectedBetweenShebangAndStatements,
70747078
statements: statements,
70757079
unexpectedBetweenStatementsAndEOFToken,
70767080
endOfFileToken: eofToken,

Sources/SwiftSyntax/generated/TokenKind.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ public enum TokenKind: Hashable {
2727
case equal
2828
case exclamationMark
2929
case floatLiteral(String)
30+
case shebang(String)
3031
case identifier(String)
3132
case infixQuestionMark
3233
case integerLiteral(String)
@@ -94,6 +95,8 @@ public enum TokenKind: Hashable {
9495
return #"!"#
9596
case .floatLiteral(let text):
9697
return text
98+
case .shebang(let text):
99+
return text
97100
case .identifier(let text):
98101
return text
99102
case .infixQuestionMark:
@@ -285,6 +288,8 @@ public enum TokenKind: Hashable {
285288
return true
286289
case .floatLiteral:
287290
return false
291+
case .shebang:
292+
return false
288293
case .identifier:
289294
return false
290295
case .infixQuestionMark:
@@ -390,6 +395,8 @@ extension TokenKind: Equatable {
390395
return true
391396
case (.floatLiteral(let lhsText), .floatLiteral(let rhsText)):
392397
return lhsText == rhsText
398+
case (.shebang(let lhsText), .shebang(let rhsText)):
399+
return lhsText == rhsText
393400
case (.identifier(let lhsText), .identifier(let rhsText)):
394401
return lhsText == rhsText
395402
case (.infixQuestionMark, .infixQuestionMark):
@@ -488,6 +495,7 @@ public enum RawTokenKind: UInt8, Equatable, Hashable {
488495
case equal
489496
case exclamationMark
490497
case floatLiteral
498+
case shebang
491499
case identifier
492500
case infixQuestionMark
493501
case integerLiteral
@@ -638,6 +646,8 @@ public enum RawTokenKind: UInt8, Equatable, Hashable {
638646
return true
639647
case .floatLiteral:
640648
return false
649+
case .shebang:
650+
return false
641651
case .identifier:
642652
return false
643653
case .infixQuestionMark:
@@ -755,6 +765,8 @@ extension TokenKind {
755765
return .exclamationMark
756766
case .floatLiteral:
757767
return .floatLiteral(text)
768+
case .shebang:
769+
return .shebang(text)
758770
case .identifier:
759771
return .identifier(text)
760772
case .infixQuestionMark:
@@ -890,6 +902,8 @@ extension TokenKind {
890902
return (.exclamationMark, nil)
891903
case .floatLiteral(let str):
892904
return (.floatLiteral, str)
905+
case .shebang(let str):
906+
return (.shebang, str)
893907
case .identifier(let str):
894908
return (.identifier, str)
895909
case .infixQuestionMark:

Sources/SwiftSyntax/generated/Tokens.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,22 @@ extension TokenSyntax {
201201
)
202202
}
203203

204+
public static func shebang(
205+
_ text: String,
206+
leadingTrivia: Trivia = [],
207+
trailingTrivia: Trivia = [],
208+
presence: SourcePresence = .present
209+
210+
) -> TokenSyntax {
211+
return TokenSyntax(
212+
.shebang(text),
213+
leadingTrivia: leadingTrivia,
214+
trailingTrivia: trailingTrivia,
215+
presence: presence
216+
217+
)
218+
}
219+
204220
public static func identifier(
205221
_ text: String,
206222
leadingTrivia: Trivia = [],

Sources/SwiftSyntax/generated/TriviaPieces.swift

Lines changed: 0 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,6 @@ public enum TriviaPiece {
4141
case newlines(Int)
4242
/// A '#' that is at the end of a line in a multi-line string literal to escape the newline.
4343
case pounds(Int)
44-
/// A script command, starting with '#!'.
45-
case shebang(String)
4644
/// A space ' ' character.
4745
case spaces(Int)
4846
/// A tab '\t' character.
@@ -84,8 +82,6 @@ extension TriviaPiece: TextOutputStreamable {
8482
printRepeated("\n", count: count)
8583
case let .pounds(count):
8684
printRepeated("#", count: count)
87-
case let .shebang(text):
88-
target.write(text)
8985
case let .spaces(count):
9086
printRepeated(" ", count: count)
9187
case let .tabs(count):
@@ -122,8 +118,6 @@ extension TriviaPiece: CustomDebugStringConvertible {
122118
return "newlines(\(data))"
123119
case .pounds(let data):
124120
return "pounds(\(data))"
125-
case .shebang(let name):
126-
return "shebang(\(name.debugDescription))"
127121
case .spaces(let data):
128122
return "spaces(\(data))"
129123
case .tabs(let data):
@@ -217,11 +211,6 @@ extension Trivia {
217211
return .pounds(1)
218212
}
219213

220-
/// Returns a piece of trivia for Shebang.
221-
public static func shebang(_ text: String) -> Trivia {
222-
return [.shebang(text)]
223-
}
224-
225214
/// Returns a piece of trivia for some number of " " characters.
226215
public static func spaces(_ count: Int) -> Trivia {
227216
return [.spaces(count)]
@@ -283,8 +272,6 @@ extension TriviaPiece {
283272
return SourceLength(utf8Length: count)
284273
case let .pounds(count):
285274
return SourceLength(utf8Length: count)
286-
case let .shebang(text):
287-
return SourceLength(of: text)
288275
case let .spaces(count):
289276
return SourceLength(utf8Length: count)
290277
case let .tabs(count):
@@ -313,7 +300,6 @@ public enum RawTriviaPiece: Equatable {
313300
case lineComment(SyntaxText)
314301
case newlines(Int)
315302
case pounds(Int)
316-
case shebang(SyntaxText)
317303
case spaces(Int)
318304
case tabs(Int)
319305
case unexpectedText(SyntaxText)
@@ -341,8 +327,6 @@ public enum RawTriviaPiece: Equatable {
341327
return .newlines(count)
342328
case let .pounds(count):
343329
return .pounds(count)
344-
case let .shebang(text):
345-
return .shebang(arena.intern(text))
346330
case let .spaces(count):
347331
return .spaces(count)
348332
case let .tabs(count):
@@ -378,8 +362,6 @@ extension TriviaPiece {
378362
self = .newlines(count)
379363
case let .pounds(count):
380364
self = .pounds(count)
381-
case let .shebang(text):
382-
self = .shebang(String(syntaxText: text))
383365
case let .spaces(count):
384366
self = .spaces(count)
385367
case let .tabs(count):
@@ -415,8 +397,6 @@ extension RawTriviaPiece {
415397
return count
416398
case let .pounds(count):
417399
return count
418-
case let .shebang(text):
419-
return text.count
420400
case let .spaces(count):
421401
return count
422402
case let .tabs(count):
@@ -450,8 +430,6 @@ extension RawTriviaPiece {
450430
return nil
451431
case .pounds(_):
452432
return nil
453-
case .shebang(let text):
454-
return text
455433
case .spaces(_):
456434
return nil
457435
case .tabs(_):

0 commit comments

Comments
 (0)