diff --git a/Sources/SwiftParser/Attributes.swift b/Sources/SwiftParser/Attributes.swift index 9919f580a94..b64d87386b1 100644 --- a/Sources/SwiftParser/Attributes.swift +++ b/Sources/SwiftParser/Attributes.swift @@ -203,12 +203,12 @@ extension Parser { case .customAttribute: shouldParseArgument = self.withLookahead { $0.atAttributeOrSpecifierArgument() } 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 9109559cee8..9ee34b05332 100644 --- a/Tests/SwiftParserTest/AttributeTests.swift +++ b/Tests/SwiftParserTest/AttributeTests.swift @@ -213,6 +213,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 b91965f308c..60ee4cd239a 100644 --- a/Tests/SwiftParserTest/DirectiveTests.swift +++ b/Tests/SwiftParserTest/DirectiveTests.swift @@ -358,7 +358,7 @@ final class DirectiveTests: ParserTestCase { ) } - func testIfConfigRRR() { + func testIfConfigAfterAttribute() { assertParse( """ @frozen1️⃣ @@ -436,4 +436,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 + """ + ) + } }