Skip to content

Commit 8267d1d

Browse files
committed
When creation of a syntax node using string interpolation failed, log the diagnostics
From the first feedback of developers starting to build macros, the number one mistake was to build incorrect syntax nodes, e.g. build an `ExprSyntax` for a declaration. Up until recently, these errors weren’t diagnosed at all. By now we at least issue an XCTest assertion if the expanded macro code contains syntax errors. But I think the issues are a lot easier to debug if we can emit an error at the location where the incorrect syntax node is being created. Since we can’t throw an error from string interpolation, use OSLog to log the parsing error to Xcode or OS console. When OSLog is not available or we are building using CMake (and thus don’t want to introduce any dependency on the SDK), silently accept the error as we do right now. Logging to stderr might be too fragile in case some application is expecting to populate stderr itself.
1 parent 338a626 commit 8267d1d

File tree

6 files changed

+61
-59
lines changed

6 files changed

+61
-59
lines changed

CodeGeneration/Sources/generate-swiftsyntax/templates/swiftsyntaxbuilder/SyntaxExpressibleByStringInterpolationConformancesFile.swift

Lines changed: 0 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -17,41 +17,8 @@ import Utils
1717

1818
let syntaxExpressibleByStringInterpolationConformancesFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
1919
DeclSyntax("import SwiftSyntax")
20-
DeclSyntax("import SwiftParser")
21-
DeclSyntax("import SwiftParserDiagnostics")
22-
23-
try! ExtensionDeclSyntax("extension SyntaxParseable") {
24-
DeclSyntax("public typealias StringInterpolation = SyntaxStringInterpolation")
25-
26-
DeclSyntax(
27-
"""
28-
public init(stringInterpolation: SyntaxStringInterpolation) {
29-
self = performParse(source: stringInterpolation.sourceText, parse: { parser in
30-
return Self.parse(from: &parser)
31-
})
32-
}
33-
"""
34-
)
35-
}
3620

3721
for node in SYNTAX_NODES where node.parserFunction != nil {
3822
DeclSyntax("extension \(node.kind.syntaxType): SyntaxExpressibleByStringInterpolation {}")
3923
}
40-
41-
DeclSyntax(
42-
"""
43-
// TODO: This should be inlined in SyntaxParseable.init(stringInterpolation:),
44-
// but is currently used in `ConvenienceInitializers.swift`.
45-
// See the corresponding TODO there.
46-
func performParse<SyntaxType: SyntaxProtocol>(source: [UInt8], parse: (inout Parser) -> SyntaxType) -> SyntaxType {
47-
return source.withUnsafeBufferPointer { buffer in
48-
var parser = Parser(buffer)
49-
// FIXME: When the parser supports incremental parsing, put the
50-
// interpolatedSyntaxNodes in so we don't have to parse them again.
51-
let result = parse(&parser)
52-
return result
53-
}
54-
}
55-
"""
56-
)
5724
}

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ let package = Package(
162162

163163
.target(
164164
name: "SwiftSyntaxBuilder",
165-
dependencies: ["SwiftBasicFormat", "SwiftParser", "SwiftParserDiagnostics", "SwiftSyntax"],
165+
dependencies: ["SwiftBasicFormat", "SwiftParser", "SwiftDiagnostics", "SwiftParserDiagnostics", "SwiftSyntax"],
166166
exclude: ["CMakeLists.txt"]
167167
),
168168

Sources/SwiftSyntaxBuilder/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ add_swift_host_library(SwiftSyntaxBuilder
1212
ResultBuilderExtensions.swift
1313
Syntax+StringInterpolation.swift
1414
SyntaxNodeWithBody.swift
15+
SyntaxParsable+ExpressibleByStringInterpolation.swift
1516
ValidatingSyntaxNodes.swift
1617
WithTrailingCommaSyntax+EnsuringTrailingComma.swift
1718

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2023 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See https://swift.org/LICENSE.txt for license information
9+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
import SwiftDiagnostics
14+
import SwiftSyntax
15+
import SwiftParser
16+
import SwiftParserDiagnostics
17+
// Don't introduce a dependency on OSLog when building SwiftSyntax using CMake
18+
// for the compiler.
19+
#if canImport(OSLog) && !CMAKE_BUILD
20+
import OSLog
21+
#endif
22+
23+
extension SyntaxParseable {
24+
public typealias StringInterpolation = SyntaxStringInterpolation
25+
26+
/// Assuming that this node contains a syntax error, log it using OSLog if we
27+
/// are on a platform that supports OSLog, otherwise don't do anything.
28+
private func logStringInterpolationParsingError() {
29+
#if canImport(OSLog) && !CMAKE_BUILD
30+
if #available(macOS 11.0, *) {
31+
let diagnostics = ParseDiagnosticsGenerator.diagnostics(for: self)
32+
let formattedDiagnostics = DiagnosticsFormatter().annotatedSource(tree: self, diags: diagnostics)
33+
Logger(subsystem: "SwiftSyntax", category: "ParseError").warning(
34+
"""
35+
Parsing a `\(Self.self)` node from string interpolation produced the following parsing errors.
36+
Set a brakpoint in `SyntaxParseable.logStringInterpolationParsingError()` to debug the failure.
37+
\(formattedDiagnostics)
38+
"""
39+
)
40+
}
41+
#endif
42+
}
43+
44+
public init(stringInterpolation: SyntaxStringInterpolation) {
45+
self = stringInterpolation.sourceText.withUnsafeBufferPointer { buffer in
46+
var parser = Parser(buffer)
47+
// FIXME: When the parser supports incremental parsing, put the
48+
// interpolatedSyntaxNodes in so we don't have to parse them again.
49+
let result = Self.parse(from: &parser)
50+
return result
51+
}
52+
if self.hasError {
53+
self.logStringInterpolationParsingError()
54+
}
55+
}
56+
}

Sources/SwiftSyntaxBuilder/generated/SyntaxExpressibleByStringInterpolationConformances.swift

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,6 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
import SwiftSyntax
16-
import SwiftParser
17-
import SwiftParserDiagnostics
18-
19-
extension SyntaxParseable {
20-
public typealias StringInterpolation = SyntaxStringInterpolation
21-
22-
public init(stringInterpolation: SyntaxStringInterpolation) {
23-
self = performParse(source: stringInterpolation.sourceText, parse: { parser in
24-
return Self.parse(from: &parser)
25-
})
26-
}
27-
}
2816

2917
extension AccessorDeclSyntax: SyntaxExpressibleByStringInterpolation {}
3018

@@ -57,16 +45,3 @@ extension StmtSyntax: SyntaxExpressibleByStringInterpolation {}
5745
extension SwitchCaseSyntax: SyntaxExpressibleByStringInterpolation {}
5846

5947
extension TypeSyntax: SyntaxExpressibleByStringInterpolation {}
60-
61-
// TODO: This should be inlined in SyntaxParseable.init(stringInterpolation:),
62-
// but is currently used in `ConvenienceInitializers.swift`.
63-
// See the corresponding TODO there.
64-
func performParse<SyntaxType: SyntaxProtocol>(source: [UInt8], parse: (inout Parser) -> SyntaxType) -> SyntaxType {
65-
return source.withUnsafeBufferPointer { buffer in
66-
var parser = Parser(buffer)
67-
// FIXME: When the parser supports incremental parsing, put the
68-
// interpolatedSyntaxNodes in so we don't have to parse them again.
69-
let result = parse(&parser)
70-
return result
71-
}
72-
}

cmake/modules/AddSwiftHostLibrary.cmake

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ function(add_swift_host_library name)
5656
-emit-module-interface-path;${module_interface_file}
5757
>)
5858

59+
target_compile_options("${name}" PRIVATE
60+
$<$<COMPILE_LANGUAGE:Swift>:-D;CMAKE_BUILD>)
61+
5962
if(CMAKE_VERSION VERSION_LESS 3.26.0 AND SWIFT_SYNTAX_ENABLE_WMO_PRE_3_26)
6063
target_compile_options(${name} PRIVATE
6164
$<$<COMPILE_LANGUAGE:Swift>:-wmo>)

0 commit comments

Comments
 (0)