{"id":26328662,"url":"https://github.com/cheekyghost-labs/syntaxsparrow","last_synced_at":"2025-03-15T21:18:06.035Z","repository":{"id":174404321,"uuid":"638969129","full_name":"CheekyGhost-Labs/SyntaxSparrow","owner":"CheekyGhost-Labs","description":"Abstraction built on top of Apple's SwiftSyntax library to traverse constituent declaration types for Swift code.","archived":false,"fork":false,"pushed_at":"2024-10-24T14:01:41.000Z","size":5884,"stargazers_count":30,"open_issues_count":0,"forks_count":3,"subscribers_count":1,"default_branch":"develop","last_synced_at":"2025-03-09T20:53:49.127Z","etag":null,"topics":["ios","ipados","macos","swift","swift-syntax","swiftsyntax","syntax","tooling","utility","xcode"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/CheekyGhost-Labs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-05-10T13:36:02.000Z","updated_at":"2025-01-13T08:12:34.000Z","dependencies_parsed_at":"2024-03-18T13:48:12.687Z","dependency_job_id":"77112a2b-bf96-46cc-97c8-23de36d9832b","html_url":"https://github.com/CheekyGhost-Labs/SyntaxSparrow","commit_stats":null,"previous_names":["cheekyghost-labs/syntaxsparrow"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CheekyGhost-Labs%2FSyntaxSparrow","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CheekyGhost-Labs%2FSyntaxSparrow/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CheekyGhost-Labs%2FSyntaxSparrow/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/CheekyGhost-Labs%2FSyntaxSparrow/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/CheekyGhost-Labs","download_url":"https://codeload.github.com/CheekyGhost-Labs/SyntaxSparrow/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243790994,"owners_count":20348386,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ios","ipados","macos","swift","swift-syntax","swiftsyntax","syntax","tooling","utility","xcode"],"created_at":"2025-03-15T21:18:05.453Z","updated_at":"2025-03-15T21:18:06.019Z","avatar_url":"https://github.com/CheekyGhost-Labs.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SyntaxSparrow\n\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FCheekyGhost-Labs%2FSyntaxSparrow%2Fbadge%3Ftype%3Dswift-versions)](https://swiftpackageindex.com/CheekyGhost-Labs/SyntaxSparrow)\n\n[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2FCheekyGhost-Labs%2FSyntaxSparrow%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/CheekyGhost-Labs/SyntaxSparrow)\n\nSyntaxSparrow is a Swift library designed to facilitate the analysis and interaction with Swift source code. It leverages SwiftSyntax to parse Swift code and produce a syntax tree which collects and traverses constituent declaration types for Swift code.\n\n## Workflows:\n\n|  Branch  | Latest Swift/Xcode |\n|:---------|:------------------:|\n| main | [![Swift Unit Tests](https://github.com/CheekyGhost-Labs/SyntaxSparrow/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/CheekyGhost-Labs/SyntaxSparrow/actions/workflows/unit-tests.yml) |\n| develop | [![Swift Unit Tests](https://github.com/CheekyGhost-Labs/SyntaxSparrow/actions/workflows/unit-tests.yml/badge.svg?branch=develop)](https://github.com/CheekyGhost-Labs/SyntaxSparrow/actions/workflows/unit-tests.yml) |\n\n- [About the Project](#about-the-project)\n- [Features](#features)\n- [Usage](#usage)\n  - [General](#general)\n  - [Updating To a New Source](#updating-to-a-new-source)\n  - [Using Constituent Declarations](#using-constituent-declarations)\n  - [Source Locations/Bounds](#source-locations-and-bounds)\n  - [Entity Types](#entity-types)\n- [Installation](#installation)\n- [Requirements](#requirements)\n- [License](#license)\n- [Contributing](#contributing)\n\n## Swift 6 Support\nSyntaxSparrow added explicit support for Swift 6 (and the Swift 6 language mode) as of version `5.0.0`. It is still compatible back to Swift 5.8, however, the package manifests for version 5.8, 5.9, and 5.10 do not include the explicit swift 6 language mode setting.\n\n## Note on Swift 5.7 Support\n\nThe latest version of the underlying [swift-syntax](https://github.com/swiftlang/swift-syntax) library no longer supports swift 5.7, as such, Syntax Sparrow will stop active support for Swift 5.7 from version 5.0.0\n\nIf you need support for 5.7, and specifically want features added after the 5.0.0 bump, you may need to fork the repo and add support yourself. If there is a backwards compatible change that can be captured as a minor update we will gladly release it via the PR process. However, if there are changes resulting in a major bump with 5.7 support, we would like to look at an alternate release process for that version (or let you maintain it on your own fork).\n\n## About the Project\nSyntaxSparrow was built on heavy inspiration from the now archived [SwiftSemantics](https://github.com/SwiftDocOrg/SwiftSemantics) project. `SwiftSemantics` was awesome, but being archived the only option is to fork and add features yourself, or hope someone has added your feature to their fork. `SyntaxSparrow` aims to pick up where this left off and add more support for conveniences, features, and harden parsing where needed.\n\nThe primary goal of producing semantic types to abstract the underlying `Syntax` expressions produced by `SwiftSyntax` remains the same, however there are a few other goals that `SyntaxSparrow` tries to achieve:\n\n- **On-request evaluation**: As some source can be quite verbose and complex, SyntaxSparrow aims to only process and iterate through nodes as you request them. The goal being to improve performance and lets the collectors focus on high-level traversal. Whether this is worth the internal trade off from a code complexity perspective will be reviewed over updates. The publicly visible semantic types are not effected by any internal updates fortunately.\n\n- **Source Locations**: `SyntaxSparrow` enables asking for where a declaration is within the provided source.\n\n- **Heirachy Based**: Rather than flatten nested declarations into a single array, Declarations in `SyntaxSparrow` are able to collect child declarations as they are supported in swift. For example, nesting structs within an enum or extensions etc\n\n- **Performance**: In the future, we aim to improve performance through more efficient parsing algorithms and data structures. This will be coupled with an expanded test suite, to ensure accuracy across a wider range of Swift code patterns and idioms. We're also looking at ways to allow users to tailor the library's behavior to their specific needs, such as customizable traversal strategies and fine-grained control over the amount of information collected.\n\n## Features\n\n- **Swift Macro Development**: Parse the raw SwiftSyntax declarations a macro provides into their semantic code to focus on your generated code.\n\n- **Swift Code Analysis**: Parse Swift code and create a syntax tree for in-depth analysis.\n\n- **Swift Code Generation**: Use parsed semantic types to generate code in a far more readable manner.\n\n- **Semantic Extraction**: Extracts various semantic structures like classes, functions, enumerations, structures, protocols, etc. from the syntax tree into constituent types.\n\n- **Source Code Updates**: Ability to update the source code on a tree instance, allowing subsequent collections as code changes.\n\n- **Different View Modes**: Control the parsing and traversal strategy when processing the source code.\n\n- **On-demand Evaluation**: The details of a semantic type are only loaded on request.\n\n- **Heirachy Based**: Semantic types support child declarations (where relevant) to allow for a more heirachy-based traversal.\n\n## Use Cases:\n\n`SyntaxSparrow` is designed to enable source exploration, and to compliment tooling to achieve some common tasks. For example:\n\n- **Code Generation**: Iterate through a readable semantic type to generate code to add to source via an IDE plugin, CLI, Swift Package Plugin etc\n\n- **Static Code Analysis**: Explore parsed source code with more accuracy to compliment code analysis tasks. i.e Resolving function names to look up index symbols and check if they are tested or unused.\n\n## Usage\n\n### General\n\nInitialize `SyntaxTree` with the path of a Swift source file, directly with a Swift source code string, or by asking to parse a `SwiftSyntax.DeclSyntaxProtocol` conforming type. Then, use the various properties of `SyntaxTree` to access the collected semantic structures. \n\n##### From Source File\n\n```swift\nlet syntaxTree = try SyntaxTree(viewMode: .fixedUp, sourceAtPath: \"/path/to/your/swift/file\")\nsyntaxTree.collectChildren()\n```\n\n##### From Source\n\n```swift\nlet syntaxTree = try SyntaxTree(viewMode: .fixedUp, sourceBuffer: \"source code\")\nsyntaxTree.collectChildren()\n```\n\n##### From SwiftSyntax.DeclSyntaxProtocol\n\n```swift\nlet syntaxTree = try SyntaxTree(viewMode: .fixedUp, declarationSyntax: declaration)\nsyntaxTree.collectChildren()\n```\n\n### Updating To a New Source:\n\nIf you want to update the source code and refresh the semantic structures:\n\n```swift\nsyntaxTree.updateToSource(newSourceCode)\nsyntaxTree.collectChildren()\n```\n\n### Using Constituent Declarations:\n\nAfter initialization and collection, you can access the collected semantic structures and their properties, such as attributes, modifiers, name, etc:\n\n```swift\nlet sourceCode = \"\"\"\nclass MyViewController: UIViewController, UICollectionViewDelegate, ListItemDisplaying {\n    \n    @available(*, unavailable, message: \"my message\")\n    enum Section {\n        case summary, people\n    }\n\n    var people: [People], places: [Place]\n    \n    var person: (name: String, age: Int)?\n    \n    weak var delegate: MyDelegate?\n\n    @IBOutlet private(set) var tableView: UITableView!\n    \n    struct MyStruct {\n\n        enum MyEnum {    \n            case sample(title: String)\n            case otherSample\n        }\n    }\n    \n    func performOperation\u003cT: Any\u003e(input: T, _ completion: (Int) -\u003e String) where T: NSFetchResult {\n        typealias SampleAlias = String\n    }\n}\n\"\"\"\nlet syntaxTree = SyntaxTree(viewMode: .fixedUp, sourceBuffer: sourceCode)\nsyntaxTree.collectChildren()\n//\nsyntaxTree.protocols[0].name // \"ListItemDisplaying\"\nsyntaxTree.protocols[0].functions[0].identifier // setListItems\nsyntaxTree.protocols[0].functions[0].signature.input[0].secondName // items\nsyntaxTree.protocols[0].functions[0].signature.input[0].isLabelOmitted // true\nsyntaxTree.protocols[0].functions[0].signature.input[0].type // .simple(\"[ListItem]\")\n\nsyntaxTree.classes[0].name // MyViewController\nsyntaxTree.classes[0].inheritance // [UIViewController, UICollectionViewDelegate, ListItemDisplaying]\nsyntaxTree.classes[0].enumerations[0].name // Section\nsyntaxTree.classes[0].enumerations[0].cases.map(\\.name) // [summary, people]\nsyntaxTree.classes[0].enumerations[0].attributes[0].name // available\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[0].name // nil\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[0].value // *\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[1].name // nil\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[1].value // unavailable\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[2].name // \"message\"\nsyntaxTree.classes[0].enumerations[0].attributes[0].arguments[2].value // \"my message\"\n\nsyntaxTree.classes[0].variables[0].name // people\nsyntaxTree.classes[0].variables[0].type // .simple(\"[People]\")\nsyntaxTree.classes[0].variables[1].name // places\nsyntaxTree.classes[0].variables[1].type // .simple(\"[Place]\")\nsyntaxTree.classes[0].variables[2].name // person\nsyntaxTree.classes[0].variables[2].type // .tuple(Tuple)\nsyntaxTree.classes[0].variables[2].isOptional // true\n\nswitch syntaxTree.classes[0].variables[1].type {\ncase .tuple(let tuple):\n    tuple.elements.map(\\.name) // [name, age]\n    tuple.isOptional // true\n}\n\nsyntaxTree.classes[0].variables[3].type // .simple(\"MyDelegate\")\nsyntaxTree.classes[0].variables[3].isOptional // true\nsyntaxTree.classes[0].variables[3].modifiers.map(\\.name) // [weak]\n\nsyntaxTree.structures[0].name // MyStruct\nsyntaxTree.structures[0].enumerations[0] // MyEnum\nsyntaxTree.structures[0].enumerations[0].cases[0].associatedValues.map(\\.name) // [title]\n\nsyntaxTree.functions[0].identifier // performOperation\nsyntaxTree.functions[0].genericParameters.map(\\.name) // [T]\nsyntaxTree.functions[0].genericParameters.map(\\.type) // [Any]\nsyntaxTree.functions[0].genericRequirements.map(\\.name) // [T]\nsyntaxTree.functions[0].genericRequirements[0].leftTypeIdentifier // T\nsyntaxTree.functions[0].genericRequirements[0].rightTypeIdentifier // NSFetchResult\nsyntaxTree.functions[0].genericRequirements[0].relation // .sameType\nsyntaxTree.functions[0].typealiases[0].name // SampleAlias\nsyntaxTree.functions[0].typealiases[0].initializedType.type // .simple(\"String\")\n```\n\n### Modifier Conveniences\n\nWhere applicable, types will conform to the `ModifierAssessing` protocol, which lets you assess what modifiers are present based on a `SwiftSyntax.Keyword` type. This is also available on any `Collection` where the element type is `Modifier`\n\n```swift\n// Assess if the instnace has `private(set)` and is `public`\nconformingInstance.containsModifierWithKeyword(.private, withDetail: \"set\")\nconformingInstance.containsModifierWithKeyword(.public)\n// or on a collection of modifiers\nvariable.modifiers.containsKeyword(.private, withDetail: \"set\")\n```\n\nsome common scenarios are available as direct getters:\n\n```swift\nconformingInstance.isPrivate\nconformingInstance.isPublic\nconformingInstance.isOpen\n// etc\nvariable.isPrivateSetter\nvariable.isFilePrivateSetter\ninitializer.isConvenience\ninitializer.isRequired\n// etc\n```\n\nConforming types are:\n\n- `Actor`\n- `Class`\n- `Enumeration`\n- `Extension`\n- `Function`\n- `ProtocolDecl`\n- `Structure`\n- `Subscript`\n- `Typealias`\n- `Variable`\n- `Initializer`\n- Any collection where the type is `Modifier`\n\n### Source Locations and Bounds:\n\n`Declaration` types can can also be sent to the `SyntaxTree` to extract source location and content:\n\n```swift\nlet sourceCode = \"\"\"\n    enum Section {\n        case summary\n        case people\n    }\n    \n    @available(*, unavailable, message: \"my message\")\n    struct MyStruct {\n        var name: String = \"name\"\n    }\n\"\"\"\nlet syntaxTree = SyntaxTree(viewMode: .fixedUp, sourceBuffer: sourceCode)\nsyntaxTree.collectChildren()\nlet sourceDetails = try syntaxTree.extractSource(forDeclaration: structure)\n\nsourceDetails.location.start.line // 5\nsourceDetails.location.start.column // 4\nsourceDetails.location.end.line // 8\nsourceDetails.location.end.line // 4\nsourceDetails.source // \"@available(*, unavailable, message: \\\"my message\\\")\\nstruct MyStruct {\\n    var name: String = \\\"name\\\"\\n}\"\n\n// The `SyntaxSourceDetails` struct also has some conveniences for calculating ranges\nsourceDetails.substringRange(in: source) // Range\u003cString.Index\u003e\nsourceDetails.stringRange(in: source) // NSRange(location: 51, length: 99)\n```\n\n### Entity Types:\n`EntityType` is a vital part of SyntaxSparrow. It represents the type of an entity within a Swift source code. These entities can include parameters, variables, function return types, etc.\n\nIt provides a structured representation for type information extracted from the Swift code. EntityType has a comprehensive support for many Swift types, handling simple, optional, tuple, function, and result types.\n\nThe various EntityType options include:\n\n- simple: Represents a simple type like `Int`, `String`, `Bool`, or any other user-defined types.\n- tuple: Represents tuple types, such as `(Int, String)`\n- closure: Represents function or closure types, like `(Int, String) -\u003e Bool`\n- Array: Represents a swift array via shorthand `[Type]` or keyword `Array\u003cType\u003e` \n- Set: Represents a swift set via keyword `Set\u003cType\u003e` \n- Dictionary: Represents a swift dictionary via shorthand `[Type: Type]` or keyword `Dictionary\u003cType, Type\u003e` \n- result: Represents Swift's Result type, capturing the Success and Failure types.\n- void: Represents a void block type. i.e `Void` or `()`\n- empty: Used to capture partial declarations where a type is not defined yet. i.e `var myName: `\n\nEntityType provides an easily accessible way to extract type-related information from your Swift source code.\n\nFor Example,\n\n```swift\nlet function = syntaxTree.functions.first\nlet returnType = function?.returnType  // This is an EntityType\n```\n\nYou can then inspect the returnType to determine its specifics\n\n```swift\nswitch returnType {\ncase .simple(let typeName):\n    print(\"Simple type: \\(typeName)\")\ncase .tuple(let tuple):\n    tuple.isOptional // true/false\n    tuple.elements // Array of `Parameter` types\ncase .array(let array):\n    array.isOptional // true/false\n    array.elementType // Entity Type\n    array.declType // .squareBrackets/.generic\ncase .set(let set):\n    set.isOptional // true/false\n    set.elementType // Entity Type\ncase .dictionary(let dict):\n    dict.isOptional // true/false\n    dict.keyType // Entity Type\n    dict.valueType // Entity Type\n    dict.declType // .squareBrackets/.generics\ncase .closure(let closure):\n    closure.input // Entity Type\n    closure.output // Entity Type\n    closure.isEscaping // true/false\n    closure.isAutoEscaping // true/false\n    closure.isOptional // true/false\n    closure.isVoidInput // true/false\n    closure.isVoidOutput // true/false\n    // see `Closure`\ncase .result(let result):\n    print(result.successType) // EntityType\n    print(result.failureType) // EntityType\n    // see `Tuple`\ncase .void(let rawType: let isOptional):\n   print(rawType) // \"Void\" or \"()?\" etc\ncase .empty:\n   print(\"undefined or partial\")\n}\n\n```\n\n## Installation\n\nCurrently, SyntaxSparrow supports Swift Package Manager (SPM).\n\nTo add SyntaxSparrow to your project, add the following line to your dependencies in your Package.swift file:\n\n```swift\n.package(url: \"https://github.com/CheekyGhost-Labs/SyntaxSparrow\", from: \"5.0.0\")\n```\n\nThen, add SyntaxSparrow as a dependency for your target:\n\n```swift\n.target(name: \"YourTarget\", dependencies: [\"SyntaxSparrow\"]),\n```\n\n## Requirements\n\n- Swift 5.7+\n- macOS 10.15+\n- iOS 13+\n\n## License\n\nSyntaxSparrow is released under the MIT License. See the LICENSE file for more information.\n\n## Contributing\n\nContributions to SyntaxSparrow are welcomed! If you have a bug to report, feel free to help out by opening a new issue or submitting a pull request.\n\nSyntaxSparrow follows pretty closely to a standard git flow process. For the most part, pull requests should be made against the `develop` branch to coordinate any releases. This also provides a means to test from the `develop` branch in the wild to further test pending releases. Once a release is ready it will be merged into `main`, tagged, and have a release branch cut.\n\n❗️❗️ **Please ensure you create any pull requests based on the develop branch** ❗️❗️\n\n#### To get started:\n\n1. **Fork the repository**: Start by creating a fork of the project to your own GitHub account.\n\n2. **Clone the forked repository**: After forking, clone your forked repository to your local machine so you can make changes.\n\n```shell\ngit clone https://github.com/CheekyGhost-Labs/SyntaxSparrow.git\n```\n\n3. **Create a new branch**: Before making changes, create a new branch for your feature or bug fix. Use a descriptive name that reflects the purpose of your changes.\n\n```shell\ngit checkout -b your-feature-branch\n```\n\n4. **Follow the Swift Language Guide**: Ensure that your code adheres to the [Swift Language Guide](https://swift.org/documentation/api-design-guidelines/) for styling and syntax conventions.\n\n5. **Make your changes**: Implement your feature or bug fix, following the project's code style and best practices. Don't forget to add tests and update documentation as needed.\n\n6. **Commit your changes**: Commit your changes with a descriptive and concise commit message. Use the imperative mood, and explain what your commit does, rather than what you did.\n\n```shell\n\n# Feature\ngit commit -m \"Feature: Adding convenience method for resolving awesomeness\"\n\n\n# Bug\ngit commit -m \"Bug: Fixing issue where awesome query was not including awesome\"\n```\n\n7. **Pull the latest changes from the upstream**: Before submitting your changes, make sure to pull the latest changes from the upstream repository and merge them into your branch. This helps to avoid any potential merge conflicts.\n\n```shell\ngit pull origin develop\n```\n\n8. **Push your changes**: Push your changes to your forked repository on GitHub.\n\n```shell\ngit push origin your-feature-branch\n```\n\n9. **Submit a pull request**: Finally, create a pull request from your forked repository to the original repository, targeting the `develop` branch. Fill in the pull request template with the necessary details, and wait for the project maintainers to review your contribution.\n\n### Unit Testing\n\nPlease ensure you add unit tests for any changes. The aim is not `100%` coverage, but rather meaningful test coverage that ensures your changes are behaving as expected without negatively effecting existing behavior.\n\nPlease note that the project maintainers may ask you to make changes to your contribution or provide additional information. Be open to feedback and willing to make adjustments as needed. Once your pull request is approved and merged, your changes will become part of the project!","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheekyghost-labs%2Fsyntaxsparrow","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcheekyghost-labs%2Fsyntaxsparrow","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheekyghost-labs%2Fsyntaxsparrow/lists"}