Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 70 additions & 5 deletions CodeGeneration/Sources/SyntaxSupport/CommonNodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ public let COMMON_NODES: [Node] = [
Node(
name: "MissingDecl",
nameForDiagnostics: "declaration",
description: "In case the source code is missing a declaration, this node stands in place of the missing declaration.",
kind: "Decl",
traits: [
"Attributed"
Expand All @@ -152,44 +153,108 @@ public let COMMON_NODES: [Node] = [
Child(
name: "Attributes",
kind: .collection(kind: "AttributeList", collectionElementName: "Attribute"),
description: "If there were standalone attributes without a declaration to attach them to, the `MissingDeclSyntax` will contain these.",
isOptional: true
),
Child(
name: "Modifiers",
kind: .collection(kind: "ModifierList", collectionElementName: "Modifier"),
description: "If there were standalone modifiers without a declaration to attach them to, the `MissingDeclSyntax` will contain these.",
isOptional: true
),
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#decl#>` that can be inserted into the source code to represent the missing declaration.
This token should always have `presence = .missing`.
"""
),
]
),

Node(
name: "MissingExpr",
nameForDiagnostics: "expression",
kind: "Expr"
description: "In case the source code is missing a expression, this node stands in place of the missing expression.",
kind: "Expr",
children: [
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#expression#>` that can be inserted into the source code to represent the missing expression.
This token should always have `presence = .missing`.
"""
)
]
),

Node(
name: "MissingPattern",
nameForDiagnostics: "pattern",
kind: "Pattern"
description: "In case the source code is missing a pattern, this node stands in place of the missing pattern.",
kind: "Pattern",
children: [
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#pattern#>` that can be inserted into the source code to represent the missing pattern.
This token should always have `presence = .missing`.
"""
)
]
),

Node(
name: "MissingStmt",
nameForDiagnostics: "statement",
kind: "Stmt"
description: "In case the source code is missing a statement, this node stands in place of the missing statement.",
kind: "Stmt",
children: [
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#statement#>` that can be inserted into the source code to represent the missing pattern.
This token should always have `presence = .missing`.
"""
)
]
),

Node(
name: "Missing",
nameForDiagnostics: nil,
kind: "Syntax"
description: "In case the source code is missing a syntax node, this node stands in place of the missing node.",
kind: "Syntax",
children: [
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#syntax#>` that can be inserted into the source code to represent the missing pattern.
This token should always have `presence = .missing`
"""
)
]
),

Node(
name: "MissingType",
nameForDiagnostics: "type",
kind: "Type"
description: "In case the source code is missing a type, this node stands in place of the missing type.",
kind: "Type",
children: [
Child(
name: "Placeholder",
kind: .token(choices: [.token(tokenKind: "IdentifierToken")], requiresLeadingSpace: false, requiresTrailingSpace: false),
description: """
A placeholder, i.e. `<#type#>` that can be inserted into the source code to represent the missing type. This token should always have `presence = .missing`.
"""
)
]
),

Node(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ import SwiftSyntaxBuilder
import SyntaxSupport
import Utils

extension Child {
public var docComment: SwiftSyntax.Trivia {
guard let description = description else {
return []
}
let dedented = dedented(string: description)
let lines = dedented.split(separator: "\n", omittingEmptySubsequences: false)
let pieces = lines.map { SwiftSyntax.TriviaPiece.docLineComment("/// \($0)") }
return Trivia(pieces: pieces)
}
}

/// This file generates the syntax nodes for SwiftSyntax. To keep the generated
/// files at a managable file size, it is to be invoked multiple times with the
/// variable `emitKind` set to a base kind listed in
Expand All @@ -33,7 +45,7 @@ func syntaxNode(emitKind: String) -> SourceFileSyntax {
"""
) {
for child in node.children {
if let childChoiceDecl = try generateSyntaxChildChoices(for: child) {
if let childChoiceDecl = try! generateSyntaxChildChoices(for: child) {
childChoiceDecl
}
}
Expand Down Expand Up @@ -140,7 +152,7 @@ func syntaxNode(emitKind: String) -> SourceFileSyntax {
if node.hasOptionalBaseTypeChild {
// TODO: Remove when we no longer support compiling in Swift 5.6. Change the
// above constructor to use `Optional<BaseType>.none` instead.
try InitializerDeclSyntax(
try! InitializerDeclSyntax(
"""
/// This initializer exists solely because Swift 5.6 does not support
/// `Optional<ConcreteType>.none` as a default value of a generic parameter.
Expand Down Expand Up @@ -191,11 +203,10 @@ func syntaxNode(emitKind: String) -> SourceFileSyntax {

let childType: String = child.kind.isNodeChoicesEmpty ? child.typeName : child.name
let type = child.isOptional ? TypeSyntax("\(raw: childType)?") : TypeSyntax("\(raw: childType)")
let childDoc = child.description.map { dedented(string: $0) }.map { Trivia.docLineComment("/// \($0)") }

try VariableDeclSyntax(
try! VariableDeclSyntax(
"""
\(raw: childDoc ?? [])
\(raw: child.docComment)
public var \(raw: child.swiftName): \(type)
"""
) {
Expand Down Expand Up @@ -274,7 +285,7 @@ private func generateSyntaxChildChoices(for child: Child) throws -> EnumDeclSynt
return nil
}

return try EnumDeclSyntax("public enum \(raw: child.name): SyntaxChildChoices") {
return try! EnumDeclSyntax("public enum \(raw: child.name): SyntaxChildChoices") {
for choice in choices {
DeclSyntax("case `\(raw: choice.swiftName)`(\(raw: choice.typeName))")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -335,5 +335,17 @@ let syntaxRewriterFile = SourceFileSyntax(leadingTrivia: copyrightHeader) {
}
"""
)

DeclSyntax(
"""
/// Rewrite `node` and anchor, making sure that the rewritten node also has
/// a parent if `node` had one.
public func rewrite(_ node: Syntax) -> Syntax {
let rewritten = self.visit(node)
let arena = SyntaxArena()
return Syntax(node.data.replacingSelf(rewritten.raw, arena: arena))
}
"""
)
}
}
24 changes: 24 additions & 0 deletions Sources/SwiftBasicFormat/generated/BasicFormat+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,18 @@ fileprivate extension AnyKeyPath {
return false
case \FunctionParameterSyntax.secondName:
return true
case \MissingDeclSyntax.placeholder:
return false
case \MissingExprSyntax.placeholder:
return false
case \MissingPatternSyntax.placeholder:
return false
case \MissingStmtSyntax.placeholder:
return false
case \MissingSyntax.placeholder:
return false
case \MissingTypeSyntax.placeholder:
return false
default:
return nil
}
Expand All @@ -262,6 +274,18 @@ fileprivate extension AnyKeyPath {
return false
case \DynamicReplacementArgumentsSyntax.forLabel:
return false
case \MissingDeclSyntax.placeholder:
return false
case \MissingExprSyntax.placeholder:
return false
case \MissingPatternSyntax.placeholder:
return false
case \MissingStmtSyntax.placeholder:
return false
case \MissingSyntax.placeholder:
return false
case \MissingTypeSyntax.placeholder:
return false
case \SwitchCaseLabelSyntax.colon:
return false
case \SwitchDefaultLabelSyntax.colon:
Expand Down
9 changes: 5 additions & 4 deletions Sources/SwiftParserDiagnostics/MissingNodesError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -335,17 +335,18 @@ extension ParseDiagnosticsGenerator {

/// Ancestors that don't contain any tokens are not very interesting to merge diagnostics (because there can't be any missing tokens we can merge them with).
/// Find the first ancestor that contains any tokens.
var ancestorWithTokens = node.parent
var ancestorWithMoreTokens = node.parent
var index = node.index
while let unwrappedParent = ancestorWithTokens, !unwrappedParent.hasTokens {
ancestorWithTokens = unwrappedParent.parent
let nodeTokens = Array(node.tokens(viewMode: .all))
while let unwrappedParent = ancestorWithMoreTokens, Array(unwrappedParent.tokens(viewMode: .all)) == nodeTokens {
ancestorWithMoreTokens = unwrappedParent.parent
index = unwrappedParent.index
}

// Walk all upcoming sibling to see if they are also missing to handle them in this diagnostic.
// If this is the case, handle all of them in this diagnostic.
var missingNodes = [Syntax(node)]
if let parentWithTokens = ancestorWithTokens {
if let parentWithTokens = ancestorWithMoreTokens {
let siblings = parentWithTokens.children(viewMode: .all)
let siblingsAfter = siblings[siblings.index(after: index)...]
for sibling in siblingsAfter {
Expand Down
12 changes: 6 additions & 6 deletions Sources/SwiftParserDiagnostics/ParseDiagnosticsGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -953,27 +953,27 @@ public class ParseDiagnosticsGenerator: SyntaxAnyVisitor {
}

public override func visit(_ node: MissingDeclSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: MissingExprSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: MissingPatternSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: MissingStmtSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: MissingSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: MissingTypeSyntax) -> SyntaxVisitorContinueKind {
return handleMissingSyntax(node)
return handleMissingSyntax(node, additionalHandledNodes: [node.placeholder.id])
}

public override func visit(_ node: OperatorDeclSyntax) -> SyntaxVisitorContinueKind {
Expand Down
56 changes: 0 additions & 56 deletions Sources/SwiftParserDiagnostics/PresenceUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,62 +60,6 @@ class PresentMaker: SyntaxRewriter {
return token
}
}

override func visit(_ node: MissingDeclSyntax) -> DeclSyntax {
let leadingTriviaBeforePlaceholder: Trivia
if node.isMissingAllTokens {
leadingTriviaBeforePlaceholder = []
} else if node.modifiers != nil {
leadingTriviaBeforePlaceholder = .space
} else {
leadingTriviaBeforePlaceholder = .newline
}
return DeclSyntax(
StructDeclSyntax(
node.unexpectedBeforeAttributes,
attributes: node.attributes,
node.unexpectedBetweenAttributesAndModifiers,
modifiers: node.modifiers,
structKeyword: .keyword(.struct, presence: .missing),
identifier: .identifier("<#declaration#>", leadingTrivia: leadingTriviaBeforePlaceholder),
memberBlock: MemberDeclBlockSyntax(
leftBrace: .leftBraceToken(presence: .missing),
members: MemberDeclListSyntax([]),
rightBrace: .rightBraceToken(presence: .missing)
)
)
)
}

override func visit(_ node: MissingExprSyntax) -> ExprSyntax {
return ExprSyntax(IdentifierExprSyntax(identifier: .identifier("<#expression#>")))
}

override func visit(_ node: MissingPatternSyntax) -> PatternSyntax {
return PatternSyntax(IdentifierPatternSyntax(identifier: .identifier("<#pattern#>")))
}

override func visit(_ node: MissingStmtSyntax) -> StmtSyntax {
return StmtSyntax(
DoStmtSyntax(
doKeyword: .keyword(.do, presence: .missing),
UnexpectedNodesSyntax([Syntax(TokenSyntax.identifier("<#statement#>"))]),
body: CodeBlockSyntax(
leftBrace: .leftBraceToken(presence: .missing),
statements: CodeBlockItemListSyntax([]),
rightBrace: .rightBraceToken(presence: .missing)
)
)
)
}

override func visit(_ node: MissingTypeSyntax) -> TypeSyntax {
return TypeSyntax(SimpleTypeIdentifierSyntax(name: .identifier("<#type#>")))
}

override func visit(_ node: MissingSyntax) -> Syntax {
return Syntax(IdentifierExprSyntax(identifier: .identifier("<#syntax#>")))
}
}

class MissingMaker: SyntaxRewriter {
Expand Down
2 changes: 2 additions & 0 deletions Sources/SwiftSyntax/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ add_swift_host_library(SwiftSyntax
BumpPtrAllocator.swift
CommonAncestor.swift
IncrementalParseTransition.swift
MemoryLayout.swift
MissingNodeInitializers.swift
Trivia.swift
SourceLength.swift
SourceLocation.swift
Expand Down
Loading