Skip to content

Parse hashbang as a child of SourceFileSyntax #1979

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/DeclNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1997,6 +1997,12 @@ public let DECL_NODES: [Node] = [
parserFunction: "parseSourceFile",
traits: ["WithStatements"],
children: [
Child(
name: "Shebang",
kind: .token(choices: [.token(.shebang)]),
documentation: "A shebang can specify the path of the compiler when using Swift source file as a script.",
isOptional: true
),
Child(
name: "Statements",
kind: .collection(kind: .codeBlockItemList, collectionElementName: "Statement")
Expand Down
3 changes: 3 additions & 0 deletions CodeGeneration/Sources/SyntaxSupport/TokenSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public enum Token: CaseIterable {
case rightParen
case rightSquare
case semicolon
case shebang
case singleQuote
case stringQuote
case stringSegment
Expand Down Expand Up @@ -209,6 +210,8 @@ public enum Token: CaseIterable {
return .punctuator(name: "rightSquare", text: "]")
case .semicolon:
return .punctuator(name: "semicolon", text: ";")
case .shebang:
return .other(name: "shebang", nameForDiagnostics: "shebang")
case .singleQuote:
return .punctuator(name: "singleQuote", text: "\'")
case .stringQuote:
Expand Down
5 changes: 0 additions & 5 deletions CodeGeneration/Sources/SyntaxSupport/Trivia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,6 @@ public let TRIVIAS: [Trivia] = [
]
),

Trivia(
name: "Shebang",
comment: #"A script command, starting with '#!'."#
),

Trivia(
name: "Space",
comment: #"A space ' ' character."#,
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftIDEUtils/SyntaxClassification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,8 @@ extension RawTokenKind {
return .none
case .semicolon:
return .none
case .shebang:
return .none
case .singleQuote:
return .stringLiteral
case .stringQuote:
Expand Down
12 changes: 6 additions & 6 deletions Sources/SwiftParser/Lexer/Cursor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,11 @@ extension Lexer.Cursor {
case UInt8(ascii: "\\"): _ = self.advance(); return Lexer.Result(.backslash)

case UInt8(ascii: "#"):
// Try lex shebang.
if self.isAtStartOfFile, self.peek(at: 1) == UInt8(ascii: "!") {
self.advanceToEndOfLine()
return Lexer.Result(.shebang)
}
// Try lex a raw string literal.
if let delimiterLength = self.advanceIfOpeningRawStringDelimiter() {
return Lexer.Result(.rawStringPoundDelimiter, stateTransition: .push(newState: .afterRawStringDelimiter(delimiterLength: delimiterLength)))
Expand Down Expand Up @@ -1171,12 +1176,6 @@ extension Lexer.Cursor {
default:
break
}
case UInt8(ascii: "#"):
guard start.isAtStartOfFile, self.advance(if: { $0 == "!" }) else {
break
}
self.advanceToEndOfLine()
continue
case UInt8(ascii: "<"), UInt8(ascii: ">"):
if self.tryLexConflictMarker(start: start) {
error = LexingDiagnostic(.sourceConflictMarker, position: start)
Expand All @@ -1189,6 +1188,7 @@ extension Lexer.Cursor {
UInt8(ascii: "}"), UInt8(ascii: "]"), UInt8(ascii: ")"),
UInt8(ascii: "@"), UInt8(ascii: ","), UInt8(ascii: ";"),
UInt8(ascii: ":"), UInt8(ascii: "\\"), UInt8(ascii: "$"),
UInt8(ascii: "#"),

// Start of integer/hex/float literals.
UInt8(ascii: "0"), UInt8(ascii: "1"), UInt8(ascii: "2"),
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftParser/Lexer/RegexLiteralLexer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,10 @@ extension Lexer.Cursor {
case .arrow, .ellipsis, .period, .atSign, .pound, .backtick, .backslash:
return false

// Shebang does not sequence expressions.
case .shebang:
return false

case .keyword:
// There are a handful of keywords that are expressions, handle them.
// Otherwise, a regex literal can generally be parsed after a keyword.
Expand Down
4 changes: 2 additions & 2 deletions Sources/SwiftParser/TokenPrecedence.swift
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,8 @@ enum TokenPrecedence: Comparable {
.dollarIdentifier, .identifier,
// '_' can occur in types to replace a type identifier
.wildcard,
// String segment, string interpolation anchor, pound, and regex pattern don't really fit anywhere else
.pound, .stringSegment, .regexLiteralPattern:
// String segment, string interpolation anchor, pound, shebang and regex pattern don't really fit anywhere else
.pound, .stringSegment, .regexLiteralPattern, .shebang:
self = .identifierLike

// MARK: Expr keyword
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftParser/TopLevel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@ extension Parser {
/// ``Parser/parse(source:parseTransition:filenameForDiagnostics:languageVersion:enableBareSlashRegexLiteral:)-7tndx``
/// API calls.
mutating func parseSourceFile() -> RawSourceFileSyntax {
let shebang = self.consume(if: .shebang)
let items = self.parseTopLevelCodeBlockItems()
let unexpectedBeforeEndOfFileToken = consumeRemainingTokens()
let endOfFile = self.consume(if: .endOfFile)!
return .init(
shebang: shebang,
statements: items,
RawUnexpectedNodesSyntax(unexpectedBeforeEndOfFileToken, arena: self.arena),
endOfFileToken: endOfFile,
Expand Down
9 changes: 0 additions & 9 deletions Sources/SwiftParser/TriviaParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,6 @@ public struct TriviaParser {
}

case UInt8(ascii: "#"):
// "#!...": .shebang
// NOTE: .shebang appears only if this trivia is at the start of the
// file. We don't know if this trivia is at the start of the file, but
// we believe that the lexer lexed it accordingly.
if position == .leading && pieces.isEmpty && cursor.advance(if: { $0 == "!" }) {
cursor.advanceToEndOfLine()
pieces.append(.shebang(start.text(upTo: cursor)))
continue
}
cursor.advance(while: { $0 == "#" })
pieces.append(.pounds(start.distance(to: cursor)))
continue
Expand Down
4 changes: 4 additions & 0 deletions Sources/SwiftParser/generated/TokenSpecStaticMembers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,10 @@ extension TokenSpec {
return TokenSpec(.semicolon)
}

static var shebang: TokenSpec {
return TokenSpec(.shebang)
}

static var singleQuote: TokenSpec {
return TokenSpec(.singleQuote)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ extension TokenKind {
return "]"
case .semicolon:
return ";"
case .shebang:
return "shebang"
case .singleQuote:
return "'"
case .stringQuote:
Expand Down
3 changes: 1 addition & 2 deletions Sources/SwiftSyntax/SourceLocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -616,8 +616,7 @@ fileprivate extension RawTriviaPiece {
body(carriageReturnLineLength)
}
lineLength = .zero
case let .shebang(text),
let .lineComment(text),
case let .lineComment(text),
let .docLineComment(text):
// Line comments are not supposed to contain newlines.
precondition(!text.containsSwiftNewline(), "line comment created that contained a new-line character")
Expand Down
2 changes: 1 addition & 1 deletion Sources/SwiftSyntax/Trivia.swift
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ extension Trivia {
return Array(repeating: TriviaPiece.carriageReturns(1), count: count)
case .carriageReturnLineFeeds(let count):
return Array(repeating: TriviaPiece.carriageReturnLineFeeds(1), count: count)
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText, .shebang:
case .lineComment, .blockComment, .docLineComment, .docBlockComment, .unexpectedText:
return [piece]
}
})
Expand Down
8 changes: 6 additions & 2 deletions Sources/SwiftSyntax/generated/ChildNameForKeyPath.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2779,8 +2779,12 @@ public func childName(_ keyPath: AnyKeyPath) -> String? {
return "constraint"
case \SomeOrAnyTypeSyntax.unexpectedAfterConstraint:
return "unexpectedAfterConstraint"
case \SourceFileSyntax.unexpectedBeforeStatements:
return "unexpectedBeforeStatements"
case \SourceFileSyntax.unexpectedBeforeShebang:
return "unexpectedBeforeShebang"
case \SourceFileSyntax.shebang:
return "shebang"
case \SourceFileSyntax.unexpectedBetweenShebangAndStatements:
return "unexpectedBetweenShebangAndStatements"
case \SourceFileSyntax.statements:
return "statements"
case \SourceFileSyntax.unexpectedBetweenStatementsAndEndOfFileToken:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7056,11 +7056,13 @@ extension SourceFileSyntax {
}
}

@available(*, deprecated, renamed: "SourceFileSyntax(leadingTrivia:_:statements:_:endOfFileToken:_:trailingTrivia:)")
@available(*, deprecated, renamed: "SourceFileSyntax(leadingTrivia:_:shebang:_:statements:_:endOfFileToken:_:trailingTrivia:)")
@_disfavoredOverload
public init(
leadingTrivia: Trivia? = nil,
_ unexpectedBeforeStatements: UnexpectedNodesSyntax? = nil,
_ unexpectedBeforeShebang: UnexpectedNodesSyntax? = nil,
shebang: TokenSyntax? = nil,
_ unexpectedBetweenShebangAndStatements: UnexpectedNodesSyntax? = nil,
statements: CodeBlockItemListSyntax,
_ unexpectedBetweenStatementsAndEOFToken: UnexpectedNodesSyntax? = nil,
eofToken: TokenSyntax = .endOfFileToken(),
Expand All @@ -7070,7 +7072,9 @@ extension SourceFileSyntax {
) {
self.init(
leadingTrivia: leadingTrivia,
unexpectedBeforeStatements,
unexpectedBeforeShebang,
shebang: shebang,
unexpectedBetweenShebangAndStatements,
statements: statements,
unexpectedBetweenStatementsAndEOFToken,
endOfFileToken: eofToken,
Expand Down
14 changes: 14 additions & 0 deletions Sources/SwiftSyntax/generated/TokenKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public enum TokenKind: Hashable {
case rightParen
case rightSquare
case semicolon
case shebang(String)
case singleQuote
case stringQuote
case stringSegment(String)
Expand Down Expand Up @@ -156,6 +157,8 @@ public enum TokenKind: Hashable {
return #"]"#
case .semicolon:
return #";"#
case .shebang(let text):
return text
case .singleQuote:
return #"'"#
case .stringQuote:
Expand Down Expand Up @@ -347,6 +350,8 @@ public enum TokenKind: Hashable {
return true
case .semicolon:
return true
case .shebang:
return false
case .singleQuote:
return true
case .stringQuote:
Expand Down Expand Up @@ -452,6 +457,8 @@ extension TokenKind: Equatable {
return true
case (.semicolon, .semicolon):
return true
case (.shebang(let lhsText), .shebang(let rhsText)):
return lhsText == rhsText
case (.singleQuote, .singleQuote):
return true
case (.stringQuote, .stringQuote):
Expand Down Expand Up @@ -519,6 +526,7 @@ public enum RawTokenKind: UInt8, Equatable, Hashable {
case rightParen
case rightSquare
case semicolon
case shebang
case singleQuote
case stringQuote
case stringSegment
Expand Down Expand Up @@ -700,6 +708,8 @@ public enum RawTokenKind: UInt8, Equatable, Hashable {
return true
case .semicolon:
return true
case .shebang:
return false
case .singleQuote:
return true
case .stringQuote:
Expand Down Expand Up @@ -843,6 +853,8 @@ extension TokenKind {
case .semicolon:
precondition(text.isEmpty || rawKind.defaultText.map(String.init) == text)
return .semicolon
case .shebang:
return .shebang(text)
case .singleQuote:
precondition(text.isEmpty || rawKind.defaultText.map(String.init) == text)
return .singleQuote
Expand Down Expand Up @@ -952,6 +964,8 @@ extension TokenKind {
return (.rightSquare, nil)
case .semicolon:
return (.semicolon, nil)
case .shebang(let str):
return (.shebang, str)
case .singleQuote:
return (.singleQuote, nil)
case .stringQuote:
Expand Down
16 changes: 16 additions & 0 deletions Sources/SwiftSyntax/generated/Tokens.swift
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,22 @@ extension TokenSyntax {
)
}

public static func shebang(
_ text: String,
leadingTrivia: Trivia = [],
trailingTrivia: Trivia = [],
presence: SourcePresence = .present

) -> TokenSyntax {
return TokenSyntax(
.shebang(text),
leadingTrivia: leadingTrivia,
trailingTrivia: trailingTrivia,
presence: presence

)
}

public static func singleQuoteToken(
leadingTrivia: Trivia = [],
trailingTrivia: Trivia = [],
Expand Down
Loading