Skip to content

Commit

Permalink
Create Extensions.swift
Browse files Browse the repository at this point in the history
  • Loading branch information
jihoonahn authored Jun 1, 2024
1 parent e0353b1 commit 65aa333
Showing 1 changed file with 286 additions and 0 deletions.
286 changes: 286 additions & 0 deletions Sources/BuilderMacros/Extensions.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2023 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
//
//===----------------------------------------------------------------------===//

import SwiftDiagnostics
import SwiftOperators
import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

extension VariableDeclSyntax {
var identifierPattern: IdentifierPatternSyntax? {
bindings.first?.pattern.as(IdentifierPatternSyntax.self)
}

var isInstance: Bool {
for modifier in modifiers {
for token in modifier.tokens(viewMode: .all) {
if token.tokenKind == .keyword(.static) || token.tokenKind == .keyword(.class) {
return false
}
}
}
return true
}

var identifier: TokenSyntax? {
identifierPattern?.identifier
}

var type: TypeSyntax? {
bindings.first?.typeAnnotation?.type
}

func accessorsMatching(_ predicate: (TokenKind) -> Bool) -> [AccessorDeclSyntax] {
let accessors: [AccessorDeclListSyntax.Element] = bindings.compactMap { patternBinding in
switch patternBinding.accessorBlock?.accessors {
case let .accessors(accessors):
return accessors
default:
return nil
}
}.flatMap { $0 }
return accessors.compactMap { predicate($0.accessorSpecifier.tokenKind) ? $0 : nil }
}

var willSetAccessors: [AccessorDeclSyntax] {
accessorsMatching { $0 == .keyword(.willSet) }
}
var didSetAccessors: [AccessorDeclSyntax] {
accessorsMatching { $0 == .keyword(.didSet) }
}

var isComputed: Bool {
if accessorsMatching({ $0 == .keyword(.get) }).count > 0 {
return true
} else {
return bindings.contains { binding in
if case .getter = binding.accessorBlock?.accessors {
return true
} else {
return false
}
}
}
}

var isImmutable: Bool {
return bindingSpecifier.tokenKind == .keyword(.let)
}

func isEquivalent(to other: VariableDeclSyntax) -> Bool {
if isInstance != other.isInstance {
return false
}
return identifier?.text == other.identifier?.text
}

var initializer: InitializerClauseSyntax? {
bindings.first?.initializer
}

func hasMacroApplication(_ name: String) -> Bool {
for attribute in attributes {
switch attribute {
case .attribute(let attr):
if attr.attributeName.tokens(viewMode: .all).map({ $0.tokenKind }) == [.identifier(name)] {
return true
}
default:
break
}
}
return false
}

func firstAttribute(for name: String) -> AttributeSyntax? {
for attribute in attributes {
switch attribute {
case .attribute(let attr):
if attr.attributeName.tokens(viewMode: .all).map({ $0.tokenKind }) == [.identifier(name)] {
return attr
}
default:
break
}
}
return nil
}
}

extension TypeSyntax {
var identifier: String? {
for token in tokens(viewMode: .all) {
switch token.tokenKind {
case .identifier(let identifier):
return identifier
default:
break
}
}
return nil
}

func genericSubstitution(_ parameters: GenericParameterListSyntax?) -> String? {
var genericParameters = [String: TypeSyntax?]()
if let parameters {
for parameter in parameters {
genericParameters[parameter.name.text] = parameter.inheritedType
}
}
var iterator = self.asProtocol(TypeSyntaxProtocol.self).tokens(viewMode: .sourceAccurate)
.makeIterator()
guard let base = iterator.next() else {
return nil
}

if let genericBase = genericParameters[base.text] {
if let text = genericBase?.identifier {
return "some " + text
} else {
return nil
}
}
var substituted = base.text

while let token = iterator.next() {
switch token.tokenKind {
case .leftAngle:
substituted += "<"
case .rightAngle:
substituted += ">"
case .comma:
substituted += ","
case .identifier(let identifier):
let type: TypeSyntax = "\(raw: identifier)"
guard let substituedType = type.genericSubstitution(parameters) else {
return nil
}
substituted += substituedType
break
default:
// ignore?
break
}
}

return substituted
}
}

extension FunctionDeclSyntax {
var isInstance: Bool {
for modifier in modifiers {
for token in modifier.tokens(viewMode: .all) {
if token.tokenKind == .keyword(.static) || token.tokenKind == .keyword(.class) {
return false
}
}
}
return true
}

struct SignatureStandin: Equatable {
var isInstance: Bool
var identifier: String
var parameters: [String]
var returnType: String
}

var signatureStandin: SignatureStandin {
var parameters = [String]()
for parameter in signature.parameterClause.parameters {
parameters.append(
parameter.firstName.text + ":"
+ (parameter.type.genericSubstitution(genericParameterClause?.parameters) ?? ""))
}
let returnType =
signature.returnClause?.type.genericSubstitution(genericParameterClause?.parameters) ?? "Void"
return SignatureStandin(
isInstance: isInstance, identifier: name.text, parameters: parameters, returnType: returnType)
}

func isEquivalent(to other: FunctionDeclSyntax) -> Bool {
return signatureStandin == other.signatureStandin
}
}

extension DeclGroupSyntax {
var memberFunctionStandins: [FunctionDeclSyntax.SignatureStandin] {
var standins = [FunctionDeclSyntax.SignatureStandin]()
for member in memberBlock.members {
if let function = member.decl.as(FunctionDeclSyntax.self) {
standins.append(function.signatureStandin)
}
}
return standins
}

func hasMemberFunction(equvalentTo other: FunctionDeclSyntax) -> Bool {
for member in memberBlock.members {
if let function = member.decl.as(FunctionDeclSyntax.self) {
if function.isEquivalent(to: other) {
return true
}
}
}
return false
}

func hasMemberProperty(equivalentTo other: VariableDeclSyntax) -> Bool {
for member in memberBlock.members {
if let variable = member.decl.as(VariableDeclSyntax.self) {
if variable.isEquivalent(to: other) {
return true
}
}
}
return false
}

var definedVariables: [VariableDeclSyntax] {
memberBlock.members.compactMap { member in
if let variableDecl = member.decl.as(VariableDeclSyntax.self) {
return variableDecl
}
return nil
}
}

func addIfNeeded(_ decl: DeclSyntax?, to declarations: inout [DeclSyntax]) {
guard let decl else { return }
if let fn = decl.as(FunctionDeclSyntax.self) {
if !hasMemberFunction(equvalentTo: fn) {
declarations.append(decl)
}
} else if let property = decl.as(VariableDeclSyntax.self) {
if !hasMemberProperty(equivalentTo: property) {
declarations.append(decl)
}
}
}

var isClass: Bool {
return self.is(ClassDeclSyntax.self)
}

var isActor: Bool {
return self.is(ActorDeclSyntax.self)
}

var isEnum: Bool {
return self.is(EnumDeclSyntax.self)
}

var isStruct: Bool {
return self.is(StructDeclSyntax.self)
}
}

0 comments on commit 65aa333

Please sign in to comment.