-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} |