Skip to content

Commit

Permalink
Adds support for swift 5.9 and Bazel
Browse files Browse the repository at this point in the history
Adds support for swift 5.9 and Bazel
  • Loading branch information
maxwellE committed Dec 5, 2023
1 parent 6db73ae commit 54f108c
Show file tree
Hide file tree
Showing 9 changed files with 374 additions and 28 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,6 @@ Packages/
Package.pins
Package.resolved
.build/

# Bazel
bazel-*
57 changes: 57 additions & 0 deletions BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
load(
"@build_bazel_rules_swift//swift:swift.bzl",
"swift_binary",
"swift_library",
)

swift_library(
name = "NeedleFoundation",
srcs = glob(
["Sources/NeedleFoundation/**/*.swift"],
allow_empty = False,
),
module_name = "NeedleFoundation",
visibility = ["//visibility:public"],
)

swift_binary(
name = "needle",
visibility = ["//visibility:public"],
deps = [
":NeedleLib",
],
)

swift_library(
name = "NeedleLib",
srcs = glob(
[
"Generator/Sources/needle/**/*.swift",
],
allow_empty = False,
),
copts = ["-suppress-warnings"],
module_name = "Needle",
deps = [
":NeedleFramework",
"@SwiftToolsSupportCore//:TSCBasic",
"@UberSwiftCommon//:CommandFramework",
],
)

swift_library(
name = "NeedleFramework",
srcs = glob(
[
"Generator/Sources/NeedleFramework/**/*.swift",
],
allow_empty = False,
),
copts = ["-suppress-warnings"],
module_name = "NeedleFramework",
deps = [
"@SwiftSyntax//:SwiftParser_opt",
"@UberSwiftCommon//:SourceParsingFramework",
"@UberSwiftConcurrency//:Concurrency",
],
)
31 changes: 18 additions & 13 deletions Generator/Sources/NeedleFramework/Parsing/BaseVisitor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,23 +28,23 @@ class BaseVisitor: SyntaxVisitor {
var varNestingLevel = 0

let filePath: String

init(filePath: String) {
self.filePath = filePath
super.init(viewMode: .sourceAccurate)
}

/// Whether we are parsing the line of code which declares a Component.
/// We need this flag to determine if the generic argument we parse later is for the Component.
var isParsingComponentDeclarationLine: Bool = false

override func visitPost(_ node: FunctionCallExprSyntax) {
if let callexpr = node.calledExpression.firstToken?.text,
let currentEntityName = currentEntityNode?.typeName {
componentToCallExprs[currentEntityName, default: []].insert(callexpr)
}
}

override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
varNestingLevel += 1
return .visitChildren
Expand All @@ -53,10 +53,11 @@ class BaseVisitor: SyntaxVisitor {
override func visitPost(_ node: VariableDeclSyntax) {
defer { varNestingLevel -= 1 }
guard let currentEntityName = currentEntityNode?.typeName, varNestingLevel == 1 else { return }
guard let entityNode = node as? EntityNode else { return }
let isExtension = currentEntityNode is ExtensionDeclSyntax
let isPublic = node.isPublic || (isExtension && currentEntityNode?.isPublic == true)
let isPrivate = node.isPrivate || currentEntityNode?.isPrivate == true
let isFileprivate = node.isFileprivate || currentEntityNode?.isFileprivate == true
let isPublic = entityNode.isPublic || (isExtension && currentEntityNode?.isPublic == true)
let isPrivate = entityNode.isPrivate || currentEntityNode?.isPrivate == true
let isFileprivate = entityNode.isFileprivate || currentEntityNode?.isFileprivate == true
let isInternal = !(isPublic || isPrivate || isFileprivate)

let memberProperties = node.bindings.compactMap { pattern -> Property? in
Expand All @@ -71,20 +72,24 @@ class BaseVisitor: SyntaxVisitor {
return Property(name: propertyName, type: propertyType, isInternal: isInternal)
}
}

propertiesDict[currentEntityName, default: []].append(contentsOf: memberProperties)
}

override func visitPost(_ node: ImportDeclSyntax) {
let importStatement = node.withoutTrivia().description.trimmed
imports.append(importStatement)
#if swift(>=5.9)
let importStatement = node.trimmed
#else
let importStatement = node.withoutTrivia()
#endif
imports.append(importStatement.description.trimmed)
}

override func visit(_ node: MemberDeclBlockSyntax) -> SyntaxVisitorContinueKind {
isParsingComponentDeclarationLine = false
return .visitChildren
}

override func visitPost(_ node: SourceFileSyntax) {
if currentEntityNode == nil {
imports = []
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import Concurrency
import Foundation
import SourceParsingFramework
#if swift(>=5.6)
#if swift(>=5.9)
import SwiftParser
#elseif swift(>=5.6)
import SwiftSyntaxParser
#else
import SwiftSyntax
Expand All @@ -42,7 +44,11 @@ class ASTProducerTask: AbstractTask<AST> {
/// - returns: The `AST` data model.
/// - throws: Any error occurred during execution.
override func execute() throws -> AST {
#if swift(>=5.9)
let syntax = try Parser.parse(source: sourceContent)
#else
let syntax = try SyntaxParser.parse(sourceUrl)
#endif
return AST(sourceHash: MD5(string: sourceContent), sourceFileSyntax: syntax, filePath: sourceUrl.path)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,17 @@ import SwiftSyntax
/// An entity node is either a Protocol or Class syntax node
protocol EntityNode: SyntaxNodeWithModifiers {
var typeName: String { get }
#if swift(>=5.9)
var inheritanceClause: InheritanceClauseSyntax? { get }
#else
var inheritanceClause: TypeInheritanceClauseSyntax? { get }
#endif
}

extension EntityNode {
/// Checks whether the entity inherits from a certain type with `typeName`
func inherits(from typeName: String) -> Bool {

let inheritedTypeSyntax = inheritanceClause?.inheritedTypeCollection.first?.typeName
// Usually, first token is the inherited type name. But sometimes it could also be the module prefix.
// In that case, we need to look for the actual type name by checking for `MemberTypeIdentifierSyntax`
Expand All @@ -37,7 +41,7 @@ extension EntityNode {
return inheritedTypeSyntax?.as(MemberTypeIdentifierSyntax.self)?.name.text == typeName
}
}

var inheritanceHasGenericArgument: Bool {
let inheritanceTypeToken = inheritanceClause?.inheritedTypeCollection.first?.typeName
return inheritanceTypeToken?.as(SimpleTypeIdentifierSyntax.self)?.genericArgumentClause != nil ||
Expand All @@ -46,24 +50,36 @@ extension EntityNode {
}

protocol SyntaxNodeWithModifiers {
#if swift(>=5.9)
var modifiers: DeclModifierListSyntax { get }
#else
var modifiers: ModifierListSyntax? { get }
#endif
}

extension SyntaxNodeWithModifiers {
var modifiersACLDeclaration: String? {
#if swift(>=5.9)
return modifiers.first?.name.text
#else
return modifiers?.first?.name.text
#endif
}

var isInternal: Bool {
modifiers?.first?.name.text == nil || modifiers?.first?.name.text == "internal"
modifiersACLDeclaration == nil || modifiersACLDeclaration == "internal"
}

var isPublic: Bool {
modifiers?.first?.name.text == "public"
modifiersACLDeclaration == "public"
}

var isPrivate: Bool {
modifiers?.first?.name.text == "private"
modifiersACLDeclaration == "private"
}

var isFileprivate: Bool {
modifiers?.first?.name.text == "fileprivate"
modifiersACLDeclaration == "fileprivate"
}
}

Expand Down Expand Up @@ -115,5 +131,3 @@ extension ExtensionDeclSyntax: EntityNode {
return extendedType.description.trimmed
}
}

extension VariableDeclSyntax: SyntaxNodeWithModifiers {}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

import XCTest
@testable import NeedleFramework
#if swift(>=5.6)
#if swift(>=5.9)
import SwiftParser
#elseif swift(>=5.6)
import SwiftSyntaxParser
#else
import SwiftSyntax
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@
import XCTest
@testable import NeedleFramework
@testable import SourceParsingFramework
#if swift(>=5.6)
#if swift(>=5.9)
import SwiftParser
#elseif swift(>=5.6)
import SwiftSyntaxParser
#else
import SwiftSyntax
Expand Down Expand Up @@ -47,12 +49,12 @@ class DeclarationsParserTaskTests: AbstractParserTests {
let imports = ["import UIKit", "import RIBs", "import Foundation", "import protocol Audio.Recordable"]
let ast = AST(sourceHash: MD5(string: sourceContent),
sourceFileSyntax: try! SyntaxParser.parse(sourceUrl), filePath: sourceUrl.path)

let task = DeclarationsParserTask(ast: ast)
let node = try! task.execute()

XCTAssertEqual(node.components.count, 3)

// Imports
XCTAssertEqual(node.imports, imports)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@

import XCTest
@testable import NeedleFramework
#if swift(>=5.6)
#if swift(>=5.9)
import SwiftParser
#elseif swift(>=5.6)
import SwiftSyntaxParser
#else
import SwiftSyntax
Expand All @@ -33,7 +35,7 @@ class PluginizedDeclarationsParserTaskTests: AbstractParserTests {

let task = PluginizedDeclarationsParserTask(ast: ast)
let node = try! task.execute()

// Imports
for statement in imports {
XCTAssertTrue(node.imports.contains(statement))
Expand Down
Loading

0 comments on commit 54f108c

Please sign in to comment.