Skip to content

Commit aac4c71

Browse files
committed
[ASTGen] Improve diagnostic infrastructure
* Infrastructure to attach notes and fix-its * Implement and improve some diagnostics
1 parent be670da commit aac4c71

11 files changed

+473
-194
lines changed

lib/ASTGen/Sources/ASTGen/Availability.swift

+36-26
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import SwiftIfConfig
2121
extension ASTGenVisitor {
2222
/// Implementation detail for `generateAvailableAttr(attribute:)` and `generateSpecializeAttr(attribute:)`.
2323
func generateAvailableAttr(
24+
attribute attrNode: AttributeSyntax,
2425
atLoc: BridgedSourceLoc,
2526
range: BridgedSourceRange,
2627
attrName: SyntaxText,
@@ -43,14 +44,15 @@ extension ASTGenVisitor {
4344
isShorthand = false
4445
}
4546
if isShorthand {
46-
return self.generateAvailableAttrShorthand(atLoc: atLoc, range: range, args: args, isSPI: isSPI)
47+
return self.generateAvailableAttrShorthand(attribute: attrNode, atLoc: atLoc, range: range, args: args, isSPI: isSPI)
4748
}
4849
}
4950

5051
// E.g.
5152
// @available(macOS, introduced: 10.12, deprecated: 11.2)
5253
// @available(*, unavailable, message: "out of service")
53-
let attr = self.generateAvailableAttrExtended(atLoc: atLoc, range: range, args: args, isSPI: isSPI)
54+
let attr = self.generateAvailableAttrExtended(attribute: attrNode, atLoc: atLoc, range: range, args: args, isSPI: isSPI)
55+
5456
if let attr {
5557
return [attr]
5658
} else {
@@ -66,6 +68,7 @@ extension ASTGenVisitor {
6668
}
6769

6870
func generateAvailableAttrShorthand(
71+
attribute attrNode: AttributeSyntax,
6972
atLoc: BridgedSourceLoc,
7073
range: BridgedSourceRange,
7174
args: AvailabilityArgumentListSyntax,
@@ -121,6 +124,7 @@ extension ASTGenVisitor {
121124
}
122125

123126
func generateAvailableAttrExtended(
127+
attribute attrNode: AttributeSyntax,
124128
atLoc: BridgedSourceLoc,
125129
range: BridgedSourceRange,
126130
args: AvailabilityArgumentListSyntax,
@@ -165,16 +169,17 @@ extension ASTGenVisitor {
165169
var renamed: BridgedStringRef? = nil
166170

167171
func generateVersion(arg: AvailabilityLabeledArgumentSyntax, into target: inout VersionAndRange?) {
168-
guard let versionSytnax = arg.value.as(VersionTupleSyntax.self) else {
169-
// TODO: Diagnose
170-
fatalError("expected version after introduced, deprecated, or obsoleted")
171-
}
172-
guard let version = VersionTuple(parsing: versionSytnax.trimmedDescription) else {
173-
// TODO: Diagnose
174-
fatalError("invalid version string")
175-
}
176172
if target != nil {
177-
// TODO: Diagnose duplicated.
173+
diagnose(.duplicatedLabeledArgumentInAttribute(attrNode, argument: arg, name: arg.label.text))
174+
return
175+
}
176+
guard
177+
let versionSytnax = arg.value.as(VersionTupleSyntax.self),
178+
let version = VersionTuple(parsing: versionSytnax.trimmedDescription)
179+
else {
180+
// FIXME: This is already diagnosed in ParserDiagnostics.
181+
// diagnose(.expectedVersionNumberInAvailableAttr(arg))
182+
return
178183
}
179184

180185
target = .init(version: version, range: self.generateSourceRange(versionSytnax))
@@ -195,7 +200,7 @@ extension ASTGenVisitor {
195200
case "noasync":
196201
attrKind = .noAsync
197202
default:
198-
// TODO: Diagnose
203+
diagnose(.unexpectedArgumentInAttribute(attrNode, arg))
199204
continue
200205
}
201206

@@ -224,31 +229,35 @@ extension ASTGenVisitor {
224229
generateVersion(arg: arg, into: &obsoleted)
225230
case .message:
226231
guard let literal = arg.value.as(SimpleStringLiteralExprSyntax.self) else {
227-
// TODO: Diagnose.
228-
fatalError("invalid argument type for 'message:'")
232+
diagnose(.expectedArgumentValueInAttribute(attrNode, label: "message", value: "string literal", at: arg.value))
233+
continue
229234
}
230235
guard let _message = self.generateStringLiteralTextIfNotInterpolated(expr: literal) else {
231-
fatalError("invalid literal value")
236+
diagnose(.stringInterpolationNotAllowedInAttribute(attrNode, at: literal))
237+
continue
232238
}
233239
guard message == nil else {
234-
fatalError("duplicated 'message' argument")
240+
diagnose(.duplicatedLabeledArgumentInAttribute(attrNode, argument: arg, name: "message"))
241+
continue
235242
}
236243
message = _message
237244
case .renamed:
238245
guard let literal = arg.value.as(SimpleStringLiteralExprSyntax.self) else {
239-
// TODO: Diagnose.
240-
fatalError("invalid argument type for 'renamed:'")
246+
diagnose(.expectedArgumentValueInAttribute(attrNode, label: "renamed", value: "string literal", at: arg.value))
247+
continue
241248
}
242249
guard let _renamed = self.generateStringLiteralTextIfNotInterpolated(expr: literal) else {
243-
fatalError("invalid literal value")
250+
diagnose(.stringInterpolationNotAllowedInAttribute(attrNode, at: literal))
251+
continue
244252
}
245253
guard renamed == nil else {
246-
fatalError("duplicated 'message' argument")
254+
diagnose(.duplicatedLabeledArgumentInAttribute(attrNode, argument: arg, name: "renamed"))
255+
continue
247256
}
248257
renamed = _renamed
249258
case .invalid:
250-
// TODO: Diagnose
251-
fatalError("invalid labeled argument")
259+
diagnose(.unexpectedArgumentInAttribute(attrNode, arg))
260+
continue
252261
}
253262
}
254263
}
@@ -333,8 +342,8 @@ extension ASTGenVisitor {
333342
// Was not a macro, it should be a valid platform name.
334343
let platform = self.generateIdentifierAndSourceLoc(domainNode)
335344
guard let version = version else {
336-
// TODO: Diagnostics.
337-
fatalError("expected version")
345+
diagnose(.expectedVersionNumberAfterPlatform(domainNode))
346+
return
338347
}
339348
// FIXME: Wasting ASTContext memory.
340349
// 'AvailabilitySpec' is 'ASTAllocated' but created spec is ephemeral in context of `@available` attributes.
@@ -361,8 +370,9 @@ extension ASTGenVisitor {
361370
case .availabilityVersionRestriction(let platformVersion):
362371
handle(domainNode: platformVersion.platform, versionNode: platformVersion.version)
363372
default:
364-
// TODO: Diagnostics.
365-
fatalError("invalid argument kind for availability spec")
373+
// FIXME: This is unreachable? ParserDiagnostics emits 'expected platform name'.
374+
let token = parsed.argument.firstToken(viewMode: .sourceAccurate)!
375+
diagnose(.unexpectedArgumentInAvailabilitySpecList(token))
366376
}
367377
}
368378

lib/ASTGen/Sources/ASTGen/BuiltinPound.swift

+16-7
Original file line numberDiff line numberDiff line change
@@ -145,18 +145,27 @@ extension ASTGenVisitor {
145145
func generatePoundAssertStmt(freestandingMacroExpansion node: some FreestandingMacroExpansionSyntax) -> BridgedPoundAssertStmt? {
146146
assert(self.ctx.langOptsHasFeature(.StaticAssert))
147147
var args = node.arguments[...]
148-
let conditionExpr = self.generateConsumingAttrOption(args: &args, label: nil) { conditionNode in
149-
self.generate(expr: conditionNode)
150-
}
151-
guard let conditionExpr else {
152-
return nil
148+
guard let arg = args.popFirst(), arg.label == nil else {
149+
// TODO: Diagnose.
150+
fatalError("expected condition expression in #assert")
153151
}
152+
let conditionExpr = self.generate(expr: arg.expression)
153+
154154
let message: BridgedStringRef?
155-
if !args.isEmpty {
156-
message = self.generateConsumingSimpleStringLiteralAttrOption(args: &args)
155+
if let arg = args.popFirst() {
156+
guard arg.label == nil else {
157+
// TODO: Diagnose.
158+
fatalError("unexpected label")
159+
}
160+
message = self.generateStringLiteralTextIfNotInterpolated(expr: arg.expression)
161+
// TODO: Diagnose if nil.
157162
} else {
158163
message = nil
159164
}
165+
guard args.isEmpty else {
166+
// TODO: Diagnose.
167+
fatalError("unexpected label")
168+
}
160169

161170
return .createParsed(
162171
self.ctx,

lib/ASTGen/Sources/ASTGen/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_pure_swift_host_library(swiftASTGen STATIC CXX_INTEROP
77
CompilerBuildConfiguration.swift
88
DeclAttrs.swift
99
Decls.swift
10+
DiagnosticMessages.swift
1011
Diagnostics.swift
1112
DiagnosticsBridge.swift
1213
Exprs.swift

0 commit comments

Comments
 (0)