@@ -196,28 +196,35 @@ extension Parser {
196
196
atSign = atSign. tokenView. withTokenDiagnostic ( tokenDiagnostic: diagnostic, arena: self . arena)
197
197
}
198
198
let attributeName = self . parseAttributeName ( )
199
+ let attributeNameHasTrailingSpace = attributeName. raw. trailingTriviaByteLength > 0
200
+
199
201
let shouldParseArgument : Bool
200
202
switch argumentMode {
201
203
case . required:
202
204
shouldParseArgument = true
203
205
case . customAttribute:
204
- shouldParseArgument = self . withLookahead { $0. atAttributeOrSpecifierArgument ( ) }
206
+ shouldParseArgument = self . withLookahead {
207
+ $0. atAttributeOrSpecifierArgument ( lastTokenHadSpace: attributeNameHasTrailingSpace, forCustomAttribute: true )
208
+ }
205
209
case . optional:
206
- shouldParseArgument = self . at ( TokenSpec ( . leftParen, allowAtStartOfLine: false ) )
210
+ shouldParseArgument = self . withLookahead {
211
+ $0. atAttributeOrSpecifierArgument ( lastTokenHadSpace: attributeNameHasTrailingSpace, forCustomAttribute: false )
212
+ }
207
213
case . noArgument:
208
214
shouldParseArgument = false
209
215
}
210
216
if shouldParseArgument {
211
217
var ( unexpectedBeforeLeftParen, leftParen) = self . expect ( TokenSpec ( . leftParen, allowAtStartOfLine: false ) )
212
- if unexpectedBeforeLeftParen == nil
213
- && ( attributeName . raw . trailingTriviaByteLength > 0 || leftParen . leadingTriviaByteLength > 0 )
214
- {
218
+
219
+ // Diagnose spaces between the name and the '('.
220
+ if unexpectedBeforeLeftParen == nil && ( attributeNameHasTrailingSpace || leftParen . leadingTriviaByteLength > 0 ) {
215
221
let diagnostic = TokenDiagnostic (
216
222
self . swiftVersion < . v6 ? . extraneousLeadingWhitespaceWarning : . extraneousLeadingWhitespaceError,
217
223
byteOffset: 0
218
224
)
219
225
leftParen = leftParen. tokenView. withTokenDiagnostic ( tokenDiagnostic: diagnostic, arena: self . arena)
220
226
}
227
+
221
228
let unexpectedBeforeArguments : RawUnexpectedNodesSyntax ?
222
229
let argument : RawAttributeSyntax . Arguments
223
230
if let parseMissingArguments, leftParen. presence == . missing {
@@ -1074,44 +1081,70 @@ extension Parser {
1074
1081
// MARK: Lookahead
1075
1082
1076
1083
extension Parser . Lookahead {
1077
- mutating func atAttributeOrSpecifierArgument( ) -> Bool {
1084
+ mutating func atAttributeOrSpecifierArgument(
1085
+ lastTokenHadSpace: Bool ,
1086
+ forCustomAttribute: Bool = false
1087
+ ) -> Bool {
1078
1088
if !self . at ( TokenSpec ( . leftParen, allowAtStartOfLine: false ) ) {
1079
1089
return false
1080
1090
}
1081
1091
1082
- var lookahead = self . lookahead ( )
1083
- lookahead. skipSingle ( )
1084
-
1085
- // If we have any keyword, identifier, or token that follows a function
1086
- // type's parameter list, this is a parameter list and not an attribute.
1087
- // Alternatively, we might have a token that illustrates we're not going to
1088
- // get anything following the attribute, which means the parentheses describe
1089
- // what follows the attribute.
1090
- switch lookahead. currentToken {
1091
- case TokenSpec ( . arrow) ,
1092
- TokenSpec ( . throw) ,
1093
- TokenSpec ( . throws) ,
1094
- TokenSpec ( . rethrows) ,
1095
- TokenSpec ( . rightParen) ,
1096
- TokenSpec ( . rightBrace) ,
1097
- TokenSpec ( . rightSquare) ,
1098
- TokenSpec ( . rightAngle) :
1099
- return false
1100
- case _ where lookahead. at ( . keyword( . async) ) :
1101
- return false
1102
- case _ where lookahead. at ( . keyword( . reasync) ) :
1103
- return false
1104
- default :
1105
- return true
1092
+ if self . swiftVersion >= . v6 {
1093
+ if !lastTokenHadSpace && currentToken. leadingTriviaByteLength == 0 {
1094
+ return true
1095
+ }
1096
+
1097
+ return withLookahead ( {
1098
+ $0. skipSingle ( )
1099
+ return $0. at ( . atSign) || $0. atStartOfDeclaration ( )
1100
+ } )
1101
+ } else {
1102
+ if !forCustomAttribute {
1103
+ return true
1104
+ }
1105
+ var lookahead = self . lookahead ( )
1106
+ lookahead. skipSingle ( )
1107
+
1108
+ // If we have any keyword, identifier, or token that follows a function
1109
+ // type's parameter list, this is a parameter list and not an attribute.
1110
+ // Alternatively, we might have a token that illustrates we're not going to
1111
+ // get anything following the attribute, which means the parentheses describe
1112
+ // what follows the attribute.
1113
+ switch lookahead. currentToken {
1114
+ case TokenSpec ( . arrow) ,
1115
+ TokenSpec ( . throw) ,
1116
+ TokenSpec ( . throws) ,
1117
+ TokenSpec ( . rethrows) ,
1118
+ TokenSpec ( . rightParen) ,
1119
+ TokenSpec ( . rightBrace) ,
1120
+ TokenSpec ( . rightSquare) ,
1121
+ TokenSpec ( . rightAngle) :
1122
+ return false
1123
+ case _ where lookahead. at ( . keyword( . async) ) :
1124
+ return false
1125
+ case _ where lookahead. at ( . keyword( . reasync) ) :
1126
+ return false
1127
+ default :
1128
+ return true
1129
+ }
1106
1130
}
1107
1131
}
1108
1132
1109
1133
mutating func canParseCustomAttribute( ) -> Bool {
1110
- guard self . canParseType ( ) else {
1134
+ guard
1135
+ let numTypeTokens = self . withLookahead ( { $0. canParseSimpleType ( ) ? $0. tokensConsumed : nil } ) ,
1136
+ numTypeTokens >= 1
1137
+ else {
1111
1138
return false
1112
1139
}
1140
+ // Check if the last token had trailing white spaces.
1141
+ for _ in 0 ..< numTypeTokens - 1 {
1142
+ self . consumeAnyToken ( )
1143
+ }
1144
+ let hasSpace = self . currentToken. trailingTriviaByteLength > 0
1145
+ self . consumeAnyToken ( )
1113
1146
1114
- if self . withLookahead ( { $0 . atAttributeOrSpecifierArgument ( ) } ) {
1147
+ if self . atAttributeOrSpecifierArgument ( lastTokenHadSpace : hasSpace , forCustomAttribute : true ) {
1115
1148
self . skipSingle ( )
1116
1149
}
1117
1150
0 commit comments