diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index 5946e76cdff..4e9f056447c 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -167,12 +167,12 @@ extension Parser { self.withLookahead { $0.atAttributeOrSpecifierArgument() } && self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) case .optional: - shouldParseArgument = self.at(.leftParen) + shouldParseArgument = self.at(TokenSpec(.leftParen, allowAtStartOfLine: false)) case .noArgument: shouldParseArgument = false } if shouldParseArgument { - var (unexpectedBeforeLeftParen, leftParen) = self.expect(.leftParen) + var (unexpectedBeforeLeftParen, leftParen) = self.expect(TokenSpec(.leftParen, allowAtStartOfLine: false)) if unexpectedBeforeLeftParen == nil && (attributeName.raw.trailingTriviaByteLength > 0 || leftParen.leadingTriviaByteLength > 0) { diff --git a/Sources/SwiftParser/Directives.swift b/Sources/SwiftParser/Directives.swift index b33c61e00b2..7fe3c89a6c9 100644 --- a/Sources/SwiftParser/Directives.swift +++ b/Sources/SwiftParser/Directives.swift @@ -213,7 +213,7 @@ extension Parser { /// Parse a line control directive. mutating func parsePoundSourceLocationDirective() -> RawPoundSourceLocationSyntax { let line = self.consumeAnyToken() - let (unexpectedBeforeLParen, lparen) = self.expect(.leftParen) + let (unexpectedBeforeLParen, lparen) = self.expect(TokenSpec(.leftParen, allowAtStartOfLine: false)) let arguments: RawPoundSourceLocationArgumentsSyntax? if !self.at(.rightParen) { let (unexpectedBeforeFile, file) = self.expect(.keyword(.file)) diff --git a/Tests/SwiftParserTest/AttributeTests.swift b/Tests/SwiftParserTest/AttributeTests.swift index 695df2b4735..a6dd0843483 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -201,6 +201,21 @@ final class AttributeTests: ParserTestCase { ) } + func testObjCAttributeNewlineParen() { + assertParse( + """ + @objc + 1️⃣(foo) func foo() + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "unexpected code '(foo)' in function" + ) + ] + ) + } + func testRethrowsAttribute() { assertParse( """ diff --git a/Tests/SwiftParserTest/AvailabilityTests.swift b/Tests/SwiftParserTest/AvailabilityTests.swift index 74c0e78e856..612c78a83c5 100644 --- a/Tests/SwiftParserTest/AvailabilityTests.swift +++ b/Tests/SwiftParserTest/AvailabilityTests.swift @@ -204,4 +204,33 @@ final class AvailabilityTests: ParserTestCase { ] ) } + + func testAvailableNewlineParen() { + assertParse( + """ + @available1️⃣ + 2️⃣(*, unavailable) func foo() {} + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '()' in attribute", + fixIts: ["insert '()'"] + ), + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected argument for '@available' attribute", + fixIts: ["insert attribute argument"] + ), + DiagnosticSpec( + locationMarker: "2️⃣", + message: "unexpected code '(*, unavailable)' in function" + ), + ], + fixedSource: """ + @available() + (*, unavailable) func foo() {} + """ + ) + } } diff --git a/Tests/SwiftParserTest/DirectiveTests.swift b/Tests/SwiftParserTest/DirectiveTests.swift index d89d1431d1f..adfa8c5bb19 100644 --- a/Tests/SwiftParserTest/DirectiveTests.swift +++ b/Tests/SwiftParserTest/DirectiveTests.swift @@ -321,4 +321,26 @@ final class DirectiveTests: ParserTestCase { ] ) } + + func testSourcelocationDirectiveNewlineParen() { + assertParse( + """ + #sourceLocation1️⃣ + (file: "other.swift", line: 1) + var someName: Int + """, + diagnostics: [ + DiagnosticSpec( + locationMarker: "1️⃣", + message: "expected '(', arguments, and ')' in '#sourceLocation' directive", + fixIts: ["insert '(', arguments, and ')'"] + ) + ], + fixedSource: """ + #sourceLocation(file: "", line: <#integer literal#>) + (file: "other.swift", line: 1) + var someName: Int + """ + ) + } }