File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -166,8 +166,28 @@ extension Parser {
166166 }
167167
168168 mutating func parseAttribute( argumentMode: AttributeArgumentMode , parseArguments: ( inout Parser ) -> RawAttributeSyntax . Argument ) -> RawAttributeListSyntax . Element {
169- let ( unexpectedBeforeAtSign, atSign) = self . expect ( . atSign)
169+ var ( unexpectedBeforeAtSign, atSign) = self . expect ( . atSign)
170170 let attributeName = self . parseType ( )
171+
172+ if !atSign. trailingTriviaPieces. isEmpty {
173+ unexpectedBeforeAtSign = RawUnexpectedNodesSyntax (
174+ combining: unexpectedBeforeAtSign, atSign,
175+ arena: self . arena
176+ )
177+ atSign = RawTokenSyntax (
178+ kind: . atSign,
179+ text: atSign. tokenText,
180+ leadingTriviaPieces: atSign. leadingTriviaPieces,
181+ presence: . missing,
182+ arena: self . arena
183+ )
184+ } else if let attributeTokenView = attributeName. raw. tokenView, attributeTokenView. leadingRawTriviaPieces. isEmpty {
185+ unexpectedBeforeAtSign = RawUnexpectedNodesSyntax (
186+ combining: unexpectedBeforeAtSign, attributeName. as ( RawTokenSyntax . self) ,
187+ arena: self . arena
188+ )
189+ }
190+
171191 let shouldParseArgument : Bool
172192 switch argumentMode {
173193 case . required:
Original file line number Diff line number Diff line change @@ -456,7 +456,31 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
456456 ] ,
457457 handledNodes: [ argument. id]
458458 )
459- return . visitChildren
459+ }
460+ if node. hasError,
461+ let unexpectedBeforeAtSignTokens = node. unexpectedBeforeAtSignToken,
462+ let unexpectedBeforeAtSign = unexpectedBeforeAtSignTokens. lastToken ( viewMode: . sourceAccurate) ,
463+ unexpectedBeforeAtSign. tokenKind == . atSign,
464+ !unexpectedBeforeAtSign. trailingTrivia. isEmpty {
465+ let tokensToDiscard = unexpectedBeforeAtSignTokens. compactMap { $0. as ( TokenSyntax . self) }
466+ addDiagnostic (
467+ unexpectedBeforeAtSign,
468+ StaticParserError . invalidWhitespaceBetweenAttributeAtSignAndIdentifier,
469+ fixIts: [
470+ FixIt (
471+ message: StaticParserFixIt . removeExtraneousWhitespace,
472+ changes: [
473+ . makeMissing( tokensToDiscard, transferTrivia: false ) ,
474+ . makePresent( node. atSignToken) ,
475+ ]
476+ )
477+ ] ,
478+ handledNodes: [
479+ node. id,
480+ unexpectedBeforeAtSignTokens. id,
481+ node. atSignToken. id
482+ ]
483+ )
460484 }
461485 return . visitChildren
462486 }
Original file line number Diff line number Diff line change @@ -164,6 +164,9 @@ extension DiagnosticMessage where Self == StaticParserError {
164164 public static var invalidWhitespaceAfterPeriod : Self {
165165 . init( " extraneous whitespace after '.' is not permitted " )
166166 }
167+ public static var invalidWhitespaceBetweenAttributeAtSignAndIdentifier : Self {
168+ . init( " extraneous whitespace after '@' is not permitted " )
169+ }
167170 public static var joinConditionsUsingComma : Self {
168171 . init( " expected ',' joining parts of a multi-clause condition " )
169172 }
Original file line number Diff line number Diff line change @@ -631,4 +631,43 @@ final class AttributeTests: XCTestCase {
631631 """
632632 )
633633 }
634+
635+ func testInvalidWhitespaceBetweenAtSignAndIdenfifierIsDiagnosed( ) {
636+ assertParse (
637+ """
638+ 1️⃣@ MyAttribute
639+ func foo() {}
640+ """ ,
641+ diagnostics: [
642+ DiagnosticSpec (
643+ message: " extraneous whitespace after '@' is not permitted " ,
644+ fixIts: [ " remove whitespace " ]
645+ )
646+ ] ,
647+ fixedSource: """
648+ @MyAttribute
649+ func foo() {}
650+ """
651+ )
652+ }
653+
654+ func testInvalidNewlineBetweenAtSignAndIdenfifierIsDiagnosed( ) {
655+ assertParse (
656+ """
657+ @
658+ MyAttribute
659+ func foo() {}
660+ """ ,
661+ diagnostics: [
662+ DiagnosticSpec (
663+ message: " extraneous whitespace after '@' is not permitted " ,
664+ fixIts: [ " remove whitespace " ]
665+ )
666+ ] ,
667+ fixedSource: """
668+ @MyAttribute
669+ func foo() {}
670+ """
671+ )
672+ }
634673}
You can’t perform that action at this time.
0 commit comments