-
Notifications
You must be signed in to change notification settings - Fork 528
Expand file tree
/
Copy pathInlineFunctionCall.swift
More file actions
92 lines (74 loc) · 2.93 KB
/
Copy pathInlineFunctionCall.swift
File metadata and controls
92 lines (74 loc) · 2.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2024 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftSyntax
public struct InlineFunctionCall: SyntaxRefactoringProvider {
public typealias Input = FunctionCallExprSyntax
public typealias Output = Syntax
public typealias Context = Void
public static func refactor(syntax: FunctionCallExprSyntax, in context: Void) throws -> Syntax {
guard let calledExpr = syntax.calledExpression.as(DeclReferenceExprSyntax.self) else {
throw RefactoringNotApplicableError("cursor must be on a function call")
}
let funcName = calledExpr.baseName.text
// Find declaration
guard let root = syntax.root.as(SourceFileSyntax.self),
let declaration = root.statements.compactMap({ $0.item.as(FunctionDeclSyntax.self) })
.first(where: { $0.name.text == funcName })
else {
throw RefactoringNotApplicableError("could not find function definition")
}
// Extract body of function
guard let body = declaration.body else {
throw RefactoringNotApplicableError("function has no body to inline")
}
// Substitution logic
var substitutionMap: [String: ExprSyntax] = [:]
let parameters = declaration.signature.parameterClause.parameters
let arguments = syntax.arguments
for (param, arg) in zip(parameters, arguments) {
let paramName = param.secondName?.text ?? param.firstName.text
substitutionMap[paramName] = arg.expression
}
let rewriter = ParameterSubstitutionRewriter(map: substitutionMap)
if body.statements.count == 1,
let returnStmt = body.statements.first?.item.as(ReturnStmtSyntax.self),
let expr = returnStmt.expression
{
let newExpr = rewriter.visit(expr)
return Syntax(newExpr)
}
let newBody = rewriter.visit(body)
let closure = ClosureExprSyntax {
newBody.statements
}
let call = FunctionCallExprSyntax(
calledExpression: ExprSyntax(closure),
leftParen: .leftParenToken(),
arguments: LabeledExprListSyntax([]),
rightParen: .rightParenToken()
)
return Syntax(call)
}
}
private class ParameterSubstitutionRewriter: SyntaxRewriter {
let map: [String: ExprSyntax]
init(map: [String: ExprSyntax]) { self.map = map }
override func visit(_ node: DeclReferenceExprSyntax) -> ExprSyntax {
if let replacement = map[node.baseName.text] {
return
replacement
.with(\.leadingTrivia, node.leadingTrivia)
.with(\.trailingTrivia, node.trailingTrivia)
}
return super.visit(node)
}
}