Last active
April 28, 2022 17:38
-
-
Save zwaldowski/7695667b2e3f94832b2ef4eda160aa82 to your computer and use it in GitHub Desktop.
SwiftSyntax: Scanning for property wrappers
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
import Foundation | |
import SwiftSyntax | |
class AtGlobalVisitor: SyntaxVisitor { | |
struct Result { | |
let name: String | |
let type: String | |
let location: SourceRange | |
let url: URL | |
} | |
let converter: SourceLocationConverter | |
let url: URL | |
var results = [Result]() | |
init(url: URL, tree: SourceFileSyntax) { | |
self.converter = SourceLocationConverter(file: url.path, tree: tree) | |
self.url = url | |
super.init() | |
walk(tree) | |
} | |
func atGlobalAttribute(from list: AttributeListSyntax) -> CustomAttributeSyntax? { | |
for attribute in list { | |
guard let customAttribute = attribute.as(CustomAttributeSyntax.self), | |
let name = customAttribute.attributeName.as(SimpleTypeIdentifierSyntax.self), | |
name.name.text == "Global" else { continue } | |
return customAttribute | |
} | |
return nil | |
} | |
func identifierBinding(from list: PatternBindingListSyntax) -> (PatternBindingSyntax, name: String)? { | |
for binding in list { | |
guard let name = binding.pattern.as(IdentifierPatternSyntax.self)?.identifier.text else { return nil } | |
return (binding, name) | |
} | |
return nil | |
} | |
func type(from attribute: CustomAttributeSyntax, _ binding: PatternBindingSyntax) -> (TypeSyntax, name: String)? { | |
guard let type = binding.typeAnnotation?.type ?? attribute.attributeName.as(SimpleTypeIdentifierSyntax.self)?.genericArgumentClause?.arguments.first?.argumentType, | |
let name = type.as(SimpleTypeIdentifierSyntax.self)?.name.text else { return nil } | |
return (type, name) | |
} | |
override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind { | |
if let attribute = node.attributes.flatMap(atGlobalAttribute), | |
let (binding, name) = identifierBinding(from: node.bindings), | |
let (_, type) = type(from: attribute, binding) { | |
let location = node.sourceRange(converter: converter) | |
results.append(Result(name: name, type: type, location: location, url: url)) | |
} | |
return .skipChildren | |
} | |
} |
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
import Darwin | |
extension CommandLine { | |
static func exit(withMessage message: String) -> Never { | |
fputs(message, stderr) | |
Darwin.exit(-1) | |
} | |
} |
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
import Foundation | |
import CoreServices | |
extension FileManager { | |
func swiftFiles(in directory: URL) -> AnySequence<URL> { | |
guard let enumerator = FileManager.default.enumerator(at: directory, includingPropertiesForKeys: [ .typeIdentifierKey ]) else { | |
return AnySequence([]) | |
} | |
let urls = enumerator.lazy.compactMap { (object) -> URL? in | |
guard let url = object as? URL, | |
let utType = try? url.resourceValues(forKeys: [ .typeIdentifierKey ]).typeIdentifier, | |
UTTypeConformsTo(utType as CFString, kUTTypeSwiftSource) else { return nil } | |
return url | |
} | |
return AnySequence(urls) | |
} | |
} |
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
import Foundation | |
import SwiftSyntax | |
guard let sourceRoot = CommandLine.arguments.dropFirst().first.flatMap(URL.init(fileURLWithPath:)) | |
?? ProcessInfo.processInfo.environment["SRCROOT"].flatMap(URL.init(fileURLWithPath:)) else { | |
CommandLine.exit(withMessage: "Could not find sources. Pass an argument to the tool or set SRCROOT in the environment.") | |
} | |
var allResults = [AtGlobalVisitor.Result]() | |
for url in FileManager.default.swiftFiles(in: sourceRoot) { | |
let tree = try SyntaxParser.parse(url) | |
let visitor = AtGlobalVisitor(url: url, tree: tree) | |
allResults += visitor.results | |
} | |
print("Found the following @Global vars: ", allResults) |
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
// swift-tools-version:5.2 | |
import PackageDescription | |
let package = Package( | |
name: "scan-dependencies", | |
platforms: [ | |
.macOS(.v10_15) | |
], | |
products: [ | |
.executable(name: "scan-dependencies", targets: ["scan-dependencies"]), | |
], | |
dependencies: [ | |
.package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", .exact("0.50200.0")), | |
], | |
targets: [ | |
.target(name: "scan-dependencies", dependencies: ["SwiftSyntax"], path: ".") | |
] | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment