Skip to content

Instantly share code, notes, and snippets.

@zwaldowski
Last active April 28, 2022 17:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zwaldowski/7695667b2e3f94832b2ef4eda160aa82 to your computer and use it in GitHub Desktop.
Save zwaldowski/7695667b2e3f94832b2ef4eda160aa82 to your computer and use it in GitHub Desktop.
SwiftSyntax: Scanning for property wrappers
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
}
}
import Darwin
extension CommandLine {
static func exit(withMessage message: String) -> Never {
fputs(message, stderr)
Darwin.exit(-1)
}
}
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)
}
}
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)
// 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