Skip to content
This repository has been archived by the owner on Sep 20, 2022. It is now read-only.

Latest commit

 

History

History
93 lines (59 loc) · 4.1 KB

CONTRIBUTING.md

File metadata and controls

93 lines (59 loc) · 4.1 KB

Contributing to SwiftInspector

👍 Thanks for taking the time to contribute to Swift Inspector! 👍

This document explains some of the guidelines around contributing to this project as well as how this project is structured, so you can jump in!

Alignment first

We ask you that if you have an idea for a new feature you open an issue.

Submitting your changes

Whenever your feature is ready for review, please open a PR with a clear list of what you've done.

For any change you make, we ask you to also add corresponding unit tests.

How to contribute

Structure of SwiftInspector

SwiftInspector is divided into four main parts:

SwiftInspector

Contains the main executable for this command line tool. It only contains the entry point main.swift file.

SwiftInspectorCommand

Contains all the files for managing commands and options for these commands. We can think of this as the frontend of this project.

SwiftInspectorAnalyzers

Comprises this project's analyzers. Any file related to analyzing Swift code should be put here. This is the layer that the Command interacts with directly.

SwiftInspectorVisitors

Comprises this project's syntax visitors. Any file that visits Swift syntax nodes should be put here. Analyzers should use the visitors in this module.

Suggested workflow

Writing a new Command

To add a new command create a YourCommand.swift file inside SwiftInspectorCommand and add it to the InspectorCommand subcommands. Your command should delegate to SwiftInspectorAnalyzer for all the logic related to analyzing Swift code.

When you're ready to write a new command, I suggest you start by writing unit tests by relying on the TestTask.swift file to create fake commands with arguments:

private struct YourNewCommand {
  fileprivate static func run(path: String, arguments: [String] = []) throws -> TaskStatus {
    let arguments = ["newcommand", "--path", path] + arguments
    return try TestTask.run(withArguments: arguments)
  }
}

Refer to the tests in the Commands target for examples.

Writing new Analyzer functionality

Since we want to separate the commands from the analyzer functionality, you should abstract your analyzer functionality in a class that lives in SwiftInspectorAnalyzer. Analyzers are a thin bridge between commands and syntax visitors – they are responsible for kicking off syntax visitation and then packaging up and returning the information gathered from syntax visitors.

Writing new Visitor functionality

Code that visits Swift syntax nodes should be live in the SwiftInspectorVisitors module.

I suggest relying on the Swift AST Explorer to understand the AST better and play around with different use cases.

When you're ready to write some code, I suggest you to start by writing unit tests by relying on the Temporary.swift file to create fake files for testing.

context("when something happens") {
  beforeEach {
    fileURL = try! Temporary.makeFile(
      content: """
               typealias SomeTypealias = TypeA & TypeB
               """
    )
  }

  it("something happens") {
    let result = try? sut.analyze(fileURL: fileURL)
    expect(result) == Something
  }
}

Refer to the tests in the Analyzer target for examples.

Things to consider:

  • We use Quick and Nimble in this repo, we rely on the following convention:
    • Use describe blocks for each internal and public method
    • Use context to setup different scenarios (e.g. "when A happens")
    • Only use one assert per test whenever possible