Full Code of uber/SwiftCodeSan for AI

master b586387590bd cached
40 files
185.8 KB
42.4k tokens
1 requests
Download .txt
Repository: uber/SwiftCodeSan
Branch: master
Commit: b586387590bd
Files: 40
Total size: 185.8 KB

Directory structure:
gitextract__6ffq4gh/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── NOTICE.txt
├── Package.swift
├── README.md
├── Sources/
│   ├── SwiftCodeSan/
│   │   ├── Executor.swift
│   │   └── main.swift
│   └── SwiftCodeSanKit/
│       ├── Core/
│       │   ├── AccessLevelRewriter.swift
│       │   ├── DeclMetaTypes.swift
│       │   ├── DeclRemover.swift
│       │   ├── DeclVisitor.swift
│       │   ├── ImportRewriter.swift
│       │   └── RefChecker.swift
│       ├── FileParsers/
│       │   └── DeclParser.swift
│       ├── FileUpdaters/
│       │   └── DeclUpdater.swift
│       ├── Operations/
│       │   ├── RemoveDeadDecls.swift
│       │   ├── RemoveUnusedImports.swift
│       │   └── UpdateAccessLevels.swift
│       └── Utils/
│           ├── Extensions/
│           │   ├── FileManagerExtensions.swift
│           │   ├── SequenceExtensions.swift
│           │   ├── StringExtensions.swift
│           │   ├── SyntaxExtensions.swift
│           │   └── SyntaxParserExtensions.swift
│           ├── Logger.swift
│           └── Scanner.swift
├── Tests/
│   ├── SwiftCodeSanTestCase.swift
│   └── TestClasses/
│       ├── Fixtures/
│       │   ├── test0.swift
│       │   ├── test1.swift
│       │   ├── test2.swift
│       │   ├── test3.swift
│       │   ├── test4.swift
│       │   ├── test5.swift
│       │   ├── test6.swift
│       │   ├── test7.swift
│       │   └── test8.swift
│       └── SwiftCodeSanTests.swift
└── install-script.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on:
  push:
    branches: [master]
  pull_request:
    branches: [master]
jobs:
  macos:
    runs-on: macOS-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v1
      - name: Build
        run: swift build -v
      - name: Test
        run: swift test -v -c release


================================================
FILE: .gitignore
================================================
## Xcode projects
*.xcodeproj
*.xcworkspace

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
.DS_Store
*.moved-aside
*.xccheckout
*.xcscmblueprint

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
Packages/
Package.pins
Package.resolved
.build/


================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mobile-open-source@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: CONTRIBUTING.md
================================================
# Contributor Covenant Code of Conduct

## Our Pledge

In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.

## Our Standards

Examples of behavior that contributes to creating a positive environment include:

* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members

Examples of unacceptable behavior by participants include:

* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting

## Our Responsibilities

Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.

Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.

## Scope

This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at mobile-open-source@uber.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.

Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]

[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/


================================================
FILE: LICENSE.txt
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: NOTICE.txt
================================================
SwiftMockGen depends on the following libraries:

Swift Package Manager (https://github.com/apple/swift-package-manager)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.


================================================
FILE: Package.swift
================================================
// swift-tools-version:5.2
import PackageDescription

let package = Package(
    name: "SwiftCodeSan",
    platforms: [
        .macOS(.v10_15),
    ],
    products: [
        .executable(name: "SwiftCodeSan", targets: ["SwiftCodeSan"]),
        .library(name: "SwiftCodeSanKit", targets: ["SwiftCodeSanKit"]),
        ],
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", .upToNextMinor(from: "1.0.2")),
        .package(name: "SwiftSyntax", url: "https://github.com/apple/swift-syntax.git", .branch("swift-5.6-RELEASE"))
    ],
    targets: [ 
        .target(
            name: "SwiftCodeSan",
            dependencies: [
                "SwiftCodeSanKit",
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
                ]),
        .target(
            name: "SwiftCodeSanKit",
            dependencies: [
                .product(name: "SwiftSyntax", package: "SwiftSyntax"),
                .product(name: "SwiftSyntaxParser", package: "SwiftSyntax"),
            ]
        ),
        .testTarget(
            name: "SwiftCodeSanTests",
            dependencies: [
                "SwiftCodeSanKit",
            ],
            path: "Tests"
        )
    ]
)



================================================
FILE: README.md
================================================
# ![](Images/logo.png)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2964/badge)](https://bestpractices.coreinfrastructure.org/projects/2964)
[![Build Status](https://github.com/uber/SwiftCodeSan/workflows/CI/badge.svg)](https://github.com/uber/SwiftCodeSan/actions)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fuber%2FSwiftCodeSan.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2Fuber%2FSwiftCodeSan?ref=badge_shield)

# Welcome to SwiftCodeSan

**SwiftCodeSan** is a tool that "sanitizes" code written in Swift.  It has support for removing dead code (unreferenced decls) and unused imports, and narrowing access levels (public to internal), which will not only help clean up the codebase but also reduce the build time and the binary size. 

It uses `SwiftSyntax` for parsing and uses concurrency for faster performance.  Unlike other tools, `SwiftCodeSan` does not involve compiling; it handles reference checks directly. This eliminates the need to compile the entire project before running an analysis, which can take a long time for codebases like Uber's (~3M LoC).

Main objectives of `SwiftCodeSan` are accuracy, performance, and ease of use.  It's a lightweight commandline tool, which uses the `SwiftCodeSanKit` framework underneath. It can be used as a standalone tool or integrated into other tools such as a linter.  Try `SwiftCodeSan` and clean up your codebase, and see an improvement in the code quality and the build time.


## Motivation

Main objectives of `SwiftCodeSan` are accuracy, performance, flexibility, and ease of use. There aren't many 3rd party tools that perform fast on a large codebase containing, for example, over 3M LoC.  They require building the entire projects on Xcode (for index stores), and take several hours to run analyses. The results contain false postives and negatives. They don't provide support to modify files directly with the results, and lack features such as finding unused imports or redundant access levels.  

`SwiftCodeSan` was built for scalability and performance so running analyses takes a few minutes instead of hours. Since it does not require compiling the codebase, it can also run on code being developed with any IDEs (not just Xcode). It's a lightweight commandline tool, and uses a minimal set of frameworks necessary (see the Used Libraries below) to keep the code lean and efficient. It provides an input option to directly modify files with results, and features other than removing dead code, such as updating access levels and removing unused import statments. 


## Disclaimer
This project may contain unstable APIs which may not be ready for general use. Support and/or new releases may be limited.


## System Requirements

* Swift 5.3 or later
* Xcode 12.0 or later
* MacOS 10.15.4 or later
* Support is included for the Swift Package Manager


## Build / Install

Option 1: Clone and build 

```
$ git clone https://github.com/uber/SwiftCodeSan.git
$ cd SwiftCodeSan
$ swift build -c release
$ .build/release/SwiftCodeSan -h  // see commandline input options below 
```

Instead of calling the binary `SwiftCodeSan` built in `.build/release`, you can copy the executable into a directory that is part of your `PATH` environment variable and call `SwiftCodeSan`.

Or use Xcode, via following.

```
$ swift package generate-xcodeproj
```

## Run

`SwiftCodeSan` is a commandline executable. To run it, pass in a list of the source file directories or file paths of a build target, and the destination filepath for the mock output. To see other arguments to the commandline, run `SwiftCodeSan --help`.

```
./SwiftCodeSan --files-to-modules [file_to_module_list] --remove-deadcode --in-place
```
The `file_to_module_list` contains a map of source file paths to corresponding module names.  Other input options are `--remove-unused-imports` and `--update-access-levels`.  If `--in-place` is set, files will be modified directly. 

Use --help to see the complete list of argument options.


## Add SwiftCodeSanKit to your project

Option 1: SPM 
```swift

dependencies: [
    .package(url: "https://github.com/uber/SwiftCodeSan.git", from: "0.0.1"),
],
targets: [
    .target(name: "MyTarget", dependencies: ["SwiftCodeSanKit"]),
]

```


## Distribution 

The `install-script.sh` will build and package up the `SwiftCodeSan` binary and other necessary resources in the same bundle. 

```
$ ./install-script.sh -h  // see input options 
$ ./install-script.sh -s [source dir] -t SwiftCodeSan -d [destination dir] -o [output filename]
```

This will create a tarball for distribution, which contains the `SwiftCodeSan` executable along with a necessary SwiftSyntax parser dylib (lib_InternalSwiftSyntaxParser.dylib). This allows running `SwiftCodeSan` without depending on where the dylib lives. 




## Used libraries

[SwiftSyntax](https://github.com/apple/swift-syntax) | 
[SPM](https://github.com/swift-package-manager)


## How to contribute to SwiftCodeSan

See [CONTRIBUTING](CONTRIBUTING.md) for more info.

## Report any issues

If you run into any problems, please file a git issue. Please include:

* The OS version (e.g. macOS 10.15.6)
* The Swift version installed on your machine (from `swift --version`)
* The Xcode version
* The specific release version of this source code (you can use `git tag` to get a list of all the release versions or `git log` to get a specific commit sha)
* Any local changes on your machine



## License

SwiftCodeSan is licensed under Apache License 2.0. See [LICENSE](LICENSE.txt) for more information.

    Copyright (C) 2017 Uber Technologies

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.


================================================
FILE: Sources/SwiftCodeSan/Executor.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import ArgumentParser
import SwiftCodeSanKit

struct Executor: ParsableCommand {
    static var configuration = CommandConfiguration(commandName: "SwiftCodeSan", abstract: "SwiftCodeSan: Code Sanitizer for Swift.")

    private enum Operation: EnumerableFlag {
        case removeDeadcode
        case removeUnusedImports
        case updateAccessLevels

        static func help(for value: Executor.Operation) -> ArgumentHelp? {
            switch value {
            case .removeDeadcode:
                return "If set, it will remove dead code and generate a report in the logfile. If an --in-place option is set, files will be modified directly."
            case .removeUnusedImports:
                return "If set, it will remove unused import statements and generate a report in the logfile. If an --in-place option is set, files will be modified directly."
            case .updateAccessLevels:
                return "If set, it will remove unnecessary public or open access levels from decls and generate a report in the logfile. If an --in-place option is set, files will be modified directly."
            }
        }
    }

    // MARK: - Private
    @Option(name: [.long, .customShort("v")],
            help: "The logging level to use. Default is set to 0 (info only). Set 1 for verbose, 2 for warning, and 3 for error.")
    private var loggingLevel: Int = 0
    @Option(name: .customLong("logfile"),
            help: "Log file path containing the analysis results. If no value is given, it will be saved to a tmp file.",
            completion: .file())
    private var logFilePath: String?
    @Option(name: [.customLong("files-to-modules"), .short],
            parsing: .upToNextOption,
            help: "File paths each containing a map of source files and corresponding module names.",
            completion: .file())
    private var fileLists: [String] = []
    @Option(name: .customLong("syslib-list"),
            parsing: .upToNextOption,
            help: "File paths each containing a list of (weak) system frameworks.",
            completion: .file())
    private var syslibLists: [String] = []
    @Option(name: .customLong("test-list"),
            parsing: .upToNextOption,
            help: "File paths each containing a list of test files.",
            completion: .file())
    private var testFileLists: [String] = []
    @Option(name: [.long, .short],
            help: "The root path. If given, it will be prepended to the source file paths.",
            completion: .file())
    private var root: String?
    @Option(name: [.long, .customShort("j")],
            help: "Maximum number of threads to execute concurrently (default = number of cores on the running machine)")
    private var concurrencyLimit: Int?

    @Option(name: [.long, .short],
            parsing: .upToNextOption,
            help: "List of declarations to whitelist (separated by a comma or a space)",
            completion: .file())
    private var whitelistDecls: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations with given prefixes to whitelist (separated by a comma or a space)",
            completion: .file())
    private var whitelistDeclsPrefix: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations with given suffixes to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistDeclsSuffix: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations with given parent types to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistParents: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations in the given modules to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistModules: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations in the modules with given prefixes to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistModulesPrefix: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of declarations in the modules with given suffixes to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistModulesSuffix: [String] = []
    @Option(name: .long,
            parsing: .upToNextOption,
            help: "List of member declarations with given names to whitelist (separated by a comma or a space).",
            completion: .file())
    private var whitelistMembers: [String] = []
    @Option(name: [.long, .short],
            help: "If set, files modified within the set number of days (leading up to today) will be whitelisted, i.e. all declarations in such files will be whitelisted.")
    private var thresholdDays: Int?

    @Flag private var operation: Operation
    @Option(name: .customLong("remove-annotation"),
            help: "If set, it will remove the annotation passed in from decls and generate a report in the logfile. If an --in-place option is set, files will be modified directly. ")
    private var deleteAnnotation: String?
    @Flag(name: [.customLong("in-place"), .short],
          help: "If set, given source files will be modified with results.")
    private var inplace: Bool = false
    @Flag(name: .customLong("in-place-tests"),
          help: "If set, given test files will be modified with results.")
    private var inplaceTests: Bool = false
    @Flag(name: .long,
          help: "If set, only top level decls will be parsed/used for analysis.")
    private var topDeclsOnly: Bool = false

    private func fullPath(_ path: String) -> String {
        if path.hasPrefix("/") {
            return path
        }
        if path.hasPrefix("~") {
            let home = FileManager.default.homeDirectoryForCurrentUser.path
            return path.replacingOccurrences(of: "~", with: home, range: path.range(of: "~"))
        }
        return FileManager.default.currentDirectoryPath + "/" + path
    }

    mutating func run() throws {
        minLogLevel = loggingLevel

        var filesToModules = [String: String]()
        fileLists.forEach { arg in
            let line = arg.components(separatedBy: ":")
            if let key = line.first, let val = line.last {
                filesToModules[key] = val
            }
        }
        
        let whitelist = Whitelist(thresholdDays: thresholdDays,
                                  decls: whitelistDecls,
                                  declsPrefix: whitelistDeclsPrefix,
                                  declsSuffix: whitelistDeclsSuffix,
                                  modules: [whitelistModules, syslibLists].compactMap{$0}.flatMap{$0},
                                  modulesPrefix: whitelistModulesPrefix,
                                  modulesSuffix: whitelistModulesSuffix,
                                  inheritedTypes: whitelistParents,
                                  members: whitelistMembers)

        execute(with: filesToModules,
                nil,
                root,
                logFilePath,
                inplace,
                inplaceTests,
                topDeclsOnly,
                concurrencyLimit,
                whitelist,
                operation,
                deleteAnnotation)
    }



    private func execute(with filesToModules: [String: String],
                         _ testfiles: [String]?,
                         _ root: String?,
                         _ logfile: String?,
                         _ inplace: Bool,
                         _ inplaceTests: Bool,
                         _ topDeclsOnly: Bool,
                         _ jobs: Int?,
                         _ whitelist: Whitelist?,
                         _ operation: Operation,
                         _ deleteAnnotation: String?) {

        switch operation {
        case .removeUnusedImports:
            removeUnusedImports(fileToModuleMap: filesToModules,
                                whitelist: whitelist,
                                topDeclsOnly: topDeclsOnly,
                                inplace: inplace,
                                logFilePath: logfile,
                                concurrencyLimit: jobs)
        case .removeDeadcode:
            removeDeadDecls(filesToModules: filesToModules,
                            whitelist: whitelist,
                            topDeclsOnly: topDeclsOnly,
                            inplace: inplace,
                            testFiles: testfiles,
                            inplaceTests: inplaceTests,
                            logFilePath: logfile,
                            concurrencyLimit: jobs,
                            onCompletion: {})
        case .updateAccessLevels:
            updateAccessLevels(filesToModules: filesToModules,
                               whitelist: whitelist,
                               inplace: inplace,
                               concurrencyLimit: jobs,
                               onCompletion: {})
        }
    }
}


================================================
FILE: Sources/SwiftCodeSan/main.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//
import ArgumentParser
import Foundation

func main() {
    let inputs = Array(CommandLine.arguments.dropFirst())

    print("Start...")
    Executor.main(inputs)
    print("Done.")
}

main()


================================================
FILE: Sources/SwiftCodeSanKit/Core/AccessLevelRewriter.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax

/**
 Updates access levels in the source code
 */
public final class AccessLevelRewriter: SyntaxRewriter {
    var decls: [DeclMetadata]
    let path: String
    let module: String
    public init(_ path: String, module: String?, decls: [DeclMetadata]) {
        self.path = path
        self.module = module ?? ""
        self.decls = decls
    }

    private func updateModifiers(_ name: String, fullName: String, description: String, declType: DeclType, modifiers: ModifierListSyntax?) -> (ModifierListSyntax, Bool)? {
        let contains = decls.contains(where: { (d: DeclMetadata) -> Bool in
            return d.name == name && d.fullName == fullName && d.declDescription == description && d.declType == declType
        })

        if contains {
            var isModified = false
            var list = [DeclModifierSyntax]()
            if let modifiers = modifiers {

                for modifier in modifiers {
                    if modifier.name.text == String.public || modifier.name.text == String.open {
                        let updatedAcl = modifier.name.withKind(.stringLiteral("")).withoutTrailingTrivia()
                        let updatedModifier = SyntaxFactory.makeDeclModifier(name: updatedAcl, detailLeftParen: modifier.detailLeftParen, detail: modifier.detail, detailRightParen: modifier.detailRightParen)
                        isModified = true
                        list.append(updatedModifier)
                    } else {

                        if isModified, modifier.name.text == String.internal, modifier.detail?.text == "set" {
                            let updatedAcl = modifier.name.withKind(.stringLiteral("")).withoutTrailingTrivia()
                            let updatedModifier = SyntaxFactory.makeDeclModifier(name: updatedAcl, detailLeftParen: nil, detail: nil, detailRightParen: nil)
                            list.append(updatedModifier)
                        } else {
                            list.append(modifier)
                        }
                    }
                }
            }
            return (SyntaxFactory.makeModifierList(list), isModified)
        }
        return nil
    }

    override public func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }

        return super.visit(node)
    }

    override public func visit(_ node: StructDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }

            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }

            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }
    
    override public func visit(_ node: InitializerDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
        var mutableNode = node
        if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
            if isModified {
                mutableNode.modifiers = updatedModifier
            }
            return DeclSyntax(mutableNode)
        }
        return super.visit(node)
    }

    override public func visit(_ node: TypealiasDeclSyntax) -> DeclSyntax {
           var mutableNode = node
           if let (updatedModifier, isModified) = updateModifiers(node.name, fullName: node.fullName, description: node.description, declType: node.declType, modifiers: node.modifiers) {
               if isModified {
                   mutableNode.modifiers = updatedModifier
               }
               return DeclSyntax(mutableNode)
           }
           return super.visit(node)
       }
}


================================================
FILE: Sources/SwiftCodeSanKit/Core/DeclMetaTypes.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax

/**
Decl metadata needed for decls in source code being parsed
*/

public typealias DeclMap = [String: [DeclMetadata]]

public enum DeclType {
    case protocolType, classType, extensionType, structType, enumType
    case typealiasType, patType
    case varType, subscriptType, funcType, initType, operatorType, enumCaseType
    case other
}

extension DeclType {
    var isEncloserType: Bool {
        if self == .protocolType ||
            self == .classType ||
            self == .extensionType ||
            self == .structType ||
            self == .enumType {
            return true
        }
        return false
    }
}

public final class DeclMetadata: Hashable {
    let name: String
    var type: String
    let fullName: String
    let declType: DeclType
    var inheritedTypes: [String]
    let boundTypes: [String]
    let boundTypesAL: [String]
    var members: [DeclMetadata] = []

    let path: String
    let module: String
    let imports: [String]
    var encloser: String
    var declDescription: String
    var annotated: Bool = false

    var isOverride: Bool
    var isExtensionMember: Bool = false
    var isPublicOrOpen: Bool
    var shouldExpose: Bool = false
    var visited: Bool = false
    var used: Bool = false

    public func hash(into hasher: inout Hasher) {
        hasher.combine(fullName)
        hasher.combine(declType)
        hasher.combine(encloser)
        hasher.combine(path)
        hasher.combine(module)
    }

    public static func == (lhs: DeclMetadata, rhs: DeclMetadata) -> Bool {
        if lhs.name == rhs.name,
            lhs.type == rhs.type,
            lhs.fullName == rhs.fullName,
            lhs.declType == rhs.declType,
            lhs.encloser == rhs.encloser,
            lhs.path == rhs.path,
            lhs.module == rhs.module {
            return true
        }
        return false
    }

    public init(path: String,
                module: String,
                imports: [String],
                encloser: String,
                name: String,
                type: String, 
                fullName: String,
                description: String,
                declType: DeclType,
                inheritedTypes: [String],
                boundTypes: [String],
                boundTypesAL: [String],
                isPublicOrOpen: Bool,
                isOverride: Bool,
                annotated: Bool = false,
                used: Bool) {
        self.path = path
        self.module = module
        self.imports = imports
        self.encloser = encloser
        self.name = name
        self.type = type
        self.fullName = fullName
        self.declDescription = description
        self.declType = declType
        self.inheritedTypes = inheritedTypes
        self.boundTypes = boundTypes
        self.boundTypesAL = boundTypesAL
        self.annotated = annotated
        self.isPublicOrOpen = isPublicOrOpen
        self.isOverride = isOverride
    }
}

struct AnnotationMetadata {
    var module: String?
    var typeAliases: [String: String]?
    var varTypes: [String: String]?
}


public struct Whitelist {
    public let thresholdDays: Int?
    public let decls: [String]?
    public let declsPrefix: [String]?
    public let declsSuffix: [String]?
    public let modules: [String]?
    public let modulesPrefix: [String]?
    public let modulesSuffix: [String]?
    public let inheritedTypes: [String]?
    public let members: [String]?

    public init(thresholdDays: Int?,
                decls: [String]?,
                 declsPrefix: [String]?,
                 declsSuffix: [String]?,
                 modules: [String]?,
                 modulesPrefix: [String]?,
                 modulesSuffix: [String]?,
                 inheritedTypes: [String]?,
                 members: [String]?) {
        self.thresholdDays = thresholdDays
        self.decls = decls
        self.declsPrefix = declsPrefix
        self.declsSuffix = declsSuffix
        self.modules = modules
        self.modulesPrefix = modulesPrefix
        self.modulesSuffix = modulesSuffix
        self.inheritedTypes = inheritedTypes
        self.members = members
    }

    func declWhitelisted(name: String, isMember: Bool, module: String?, parents: [String]?, path: String?) -> Bool {
        if let module = module {
            if let list = modules, list.contains(module) {
                return true
            }

            if let list = modulesPrefix {
                let moduleHasPrefix = !list.filter{module.hasPrefix($0)}.isEmpty
                if moduleHasPrefix { return true }
            }

            if let list = modulesSuffix {
                let moduleHasSuffix = !list.filter{module.hasSuffix($0)}.isEmpty
                if moduleHasSuffix { return true }
            }
        }

        if let parents = parents, let list = inheritedTypes {
            let inParentsList = !list.filter{ parents.contains($0) }.isEmpty
            if inParentsList { return true }
        }

        if isMember {
            if let list = members, list.contains(name) { return true }
        } else {
            if let list = decls, list.contains(name) { return true }
            if let list = declsPrefix {
                let declHasPrefix = !list.filter { name.hasPrefix($0) }.isEmpty
                if declHasPrefix { return true }
            }

            if let list = declsSuffix {
                let declHasSuffix = !list.filter { name.hasSuffix($0) }.isEmpty
                if declHasSuffix { return true }
            }
        }

        return false
    }
}


public func flatten(declMap: DeclMap) -> DeclMap {
    var flatDeclMap = DeclMap()

    for (k, vals) in declMap {
        for v in vals {
            if flatDeclMap[k] == nil {
                flatDeclMap[k] = []
            }

            if flatDeclMap[k]?.contains(v) ?? false {
            } else {
                flatDeclMap[k]?.append(v)
            }

            for m in v.members {
                if flatDeclMap[m.name] == nil {
                    flatDeclMap[m.name] = []
                }
                flatDeclMap[m.name]?.append(m)
            }
        }
    }
    return flatDeclMap
}


================================================
FILE: Sources/SwiftCodeSanKit/Core/DeclRemover.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//


import Foundation
import SwiftSyntax

/**
Removes unused decls
*/

public final class DeclRemover: SyntaxRewriter {
    let path: String
    let decls: [DeclMetadata]

    public init(_ path: String, decls: [DeclMetadata]) {
        self.path = path
        self.decls = decls
    }

    override public func visit(_ node: ExtensionDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankExtensionDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: EnumDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankEnumDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: StructDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankStructDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: ProtocolDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankProtocolDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: ClassDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankClassDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: FunctionDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankFunctionDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: SubscriptDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankSubscriptDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: InitializerDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankInitializerDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: VariableDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankVariableDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: TypealiasDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankTypealiasDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: AssociatedtypeDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankAssociatedtypeDecl())
        }
        return super.visit(node)
    }
    override public func visit(_ node: EnumCaseDeclSyntax) -> DeclSyntax {
        if shouldRemove(node.name, fullName: node.fullName, description: node.description, declType: node.declType) {
            return DeclSyntax(SyntaxFactory.makeBlankEnumCaseDecl())
        }
        return super.visit(node)
    }

    private func shouldRemove(_ name: String, fullName: String, description: String, declType: DeclType) -> Bool {
        let inList = decls.contains(where: { (d: DeclMetadata) -> Bool in
            return d.name == name && d.fullName == fullName && d.declDescription == description && d.declType == declType
        })
        return inList
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Core/DeclVisitor.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax

/**
Visit decls in source code being parsed
*/

final class DeclVisitor: SyntaxVisitor {
    var declMap = DeclMap()
    let path: String
    let module: String
    let topDeclsOnly: Bool
    let whitelistPath: Bool
    let whitelist: Whitelist?
    var importedModules = [String]()

    init(_ path: String,
         module: String?,
         topDeclsOnly: Bool,
         whitelistPath: Bool,
         whitelist: Whitelist?) {
        self.whitelist = whitelist
        self.whitelistPath = whitelistPath
        self.path = path
        self.module = module ?? ""
        self.topDeclsOnly = topDeclsOnly
    }


    override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
        updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members)
        return .skipChildren
    }

    override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
        if node.attributesDescription.contains(String.propertyWrapper) {
            return .skipChildren
        }

        updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members)
        return .visitChildren
    }
    override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
        if node.attributesDescription.contains(String.propertyWrapper) {
            return .skipChildren
        }

        updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members)
        return .skipChildren
    }
    override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
        updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members)
        return .skipChildren
    }
    override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
        updateDecl(node, description: node.description, members: topDeclsOnly ? nil : node.members.members)
        return .skipChildren
    }
    override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind {
        importedModules.append(node.path.description.trimmed)
        return .visitChildren
    }

    override func visit(_ node: CodeBlockItemSyntax) -> SyntaxVisitorContinueKind {
        if let item = node.item.as(FunctionDeclSyntax.self) {
            updateDecl(item, description: item.description, members: nil)
            return .skipChildren
        } else if let _ = node.item.as(OperatorDeclSyntax.self) {
            return .skipChildren
        } else if let item = node.item.as(VariableDeclSyntax.self) {
            updateDecl(item, description: item.description, members: nil)
            return .skipChildren
        } else if let item = node.item.as(TypealiasDeclSyntax.self) {
            updateDecl(item, description: item.description, members: nil)
            return .skipChildren
        }

        return .visitChildren
    }

    private func memberDecls(_ decl: DeclSyntax, encloser: String, encloserDeclType: DeclType, encloserWhitelisted: Bool) -> [DeclMetadata] {
        let mdecls = decl.declMetadatas(path: path, module: module, encloser: encloser, description: decl.description, imports: importedModules)

        for mdecl in mdecls {
            if encloserDeclType == .extensionType {
                mdecl.isExtensionMember = true
            }

            let memberWhitelisted = whitelist?.declWhitelisted(name: mdecl.name, isMember: true, module: nil, parents: nil, path: mdecl.path) ?? false
            if encloserWhitelisted ||
                memberWhitelisted ||
                mdecl.declType == .initType ||
                mdecl.declType == .subscriptType ||
                mdecl.declType == .operatorType {
                if mdecl.isPublicOrOpen {
                    mdecl.shouldExpose = true
                }
                mdecl.used = true
            }
        }
        return mdecls
    }

    private func updateDecl(_ item: DeclProtocol, description: String, members: MemberDeclListSyntax?) {
        let decls = item.declMetadatas(path: path, module: module, encloser: "", description: description, imports: importedModules)

        for decl in decls {
            var shouldWhitelist = (decl.declType == .operatorType)
            if !shouldWhitelist, !decl.name.isEmpty {
                if let whitelist = whitelist, whitelist.declWhitelisted(name: decl.name, isMember: false, module: module, parents: decl.inheritedTypes, path: decl.path) {
                    // whitelisted so don't add to declMap
                    shouldWhitelist = true
                }
            }

            if shouldWhitelist {
                if decl.isPublicOrOpen {
                    decl.shouldExpose = true
                }
                decl.used = true
            }

            if let members = members {
                var list = [DeclMetadata]()
                for m in members {
                    if let ifconfig = m.decl.as(IfConfigDeclSyntax.self) {
                        for clause in ifconfig.clauses {
                            if let clauseMembers = clause.elements.as(MemberDeclListSyntax.self) {
                                for el in clauseMembers {
                                    let mdecls = memberDecls(el.decl, encloser: decl.name, encloserDeclType: decl.declType, encloserWhitelisted: shouldWhitelist)
                                    list.append(contentsOf: mdecls)
                                }
                            }
                        }
                    } else {
                        let mdecls = memberDecls(m.decl, encloser: decl.name, encloserDeclType: decl.declType, encloserWhitelisted: shouldWhitelist)
                        list.append(contentsOf: mdecls)
                    }
                }
                decl.members = list
            }

            if !decl.name.isEmpty, declMap[decl.name] == nil {
                declMap[decl.name] = []
            }

            declMap[decl.name]?.append(decl)
        }
    }
}



================================================
FILE: Sources/SwiftCodeSanKit/Core/ImportRewriter.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax


/**
Updates import statements in source code
*/

public final class ImportRewriter: SyntaxRewriter {
    let unused: [String]

    public init(_ path: String, unusedModules: [String]?) {
        self.unused = unusedModules ?? []
    }

    override public func visit(_ node: ImportDeclSyntax) -> DeclSyntax {
        var remove = false
        let str = node.path.description.trimmed
        if unused.contains(str) {
            remove = true
        } else {
            for t in node.path.tokens {
                if unused.contains(t.text) {
                    remove = true
                }
            }
        }

        if remove {
            if let trivia = node.importTok.leadingTrivia {
                let t = SyntaxFactory.makeUnknown("", leadingTrivia: trivia, trailingTrivia: Trivia(pieces: []))
                let ret = SyntaxFactory.makeImportDecl(attributes: nil, modifiers: nil, importTok: t, importKind: nil, path: SyntaxFactory.makeAccessPath([]))
                return DeclSyntax(ret)
            } else {
                let ret = SyntaxFactory.makeBlankImportDecl()
                return DeclSyntax(ret)
            }
        }

        return super.visit(node)
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Core/RefChecker.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax

/**
Checks for referenced decls
*/

final class RefChecker: SyntaxVisitor {
    var imports = [String]()
    private var declMap = DeclMap()
    private var path: String
    private var module: String
    private var reflist = [String]()
    var refs: Set<String> {
        return Set(reflist)
    }
    
    init(_ path: String, module: String, declMap: DeclMap) {
        self.path = path
        self.module = module
        self.declMap = declMap
    }

    override func visit(_ node: CodeBlockItemSyntax) -> SyntaxVisitorContinueKind {
        if node.item.is(ExprSyntax.self) || node.item.is(StmtSyntax.self) {
            reflist.append(contentsOf: node.item.refTypes(with: declMap))
            return .skipChildren
        }

        return .visitChildren
    }

    override func visit(_ node: VariableDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        if node.isOverride {
            reflist.append(node.name)
        }

        return .visitChildren
    }

    override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        if node.isOverride {
            reflist.append(node.name)
        }
        return .visitChildren
    }

    override func visit(_ node: SubscriptDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }

    override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        if node.isOverride {
            reflist.append(node.name)
        }
        return .visitChildren
    }

    override func visit(_ node: EnumCaseDeclSyntax) -> SyntaxVisitorContinueKind {
         reflist.append(contentsOf: node.refTypes(with: declMap))
         return .visitChildren
     }

    override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }
    override func visit(_ node: ProtocolDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }
    override func visit(_ node: ExtensionDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        reflist.append(node.name)
        return .visitChildren
    }
    override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }
    override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }

    override func visit(_ node: TypealiasDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }

    override func visit(_ node: AssociatedtypeDeclSyntax) -> SyntaxVisitorContinueKind {
        reflist.append(contentsOf: node.refTypes(with: declMap))
        return .visitChildren
    }

    override func visit(_ node: ImportDeclSyntax) -> SyntaxVisitorContinueKind {
        if node.attributes == nil, node.importKind == nil {
            let str = node.path.description.trimmed
            imports.append(str)
        }
        return .skipChildren
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/FileParsers/DeclParser.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import SwiftSyntax
import SwiftSyntaxParser

public class DeclParser {
    
    public init() {}

    func scanAndMapDecls(fileToModuleMap: [String: String],
                         topDeclsOnly: Bool,
                         whitelist: Whitelist?) -> DeclMap {
        var allDeclMap = DeclMap()

        scanDecls(fileToModuleMap: fileToModuleMap, topDeclsOnly: topDeclsOnly, whitelist: whitelist) { (filepath, subResults) in
            for (k, decls) in subResults {
                if allDeclMap[k] == nil {
                    allDeclMap[k] = []
                }

                for decl in decls {
                    if allDeclMap[k]?.contains(decl) ?? false {
                        // Already added, so do nothing
                    } else {
                        allDeclMap[k]?.append(decl)
                    }
                }
            }
        }
        return allDeclMap
    }

    func scanAndMapDecls(fileToModuleMap: [String: String],
                         topDeclsOnly: Bool) -> DeclMap {
        return scanAndMapDecls(fileToModuleMap: fileToModuleMap,
                               topDeclsOnly: topDeclsOnly,
                               whitelist: nil)
    }

    func scanDecls(fileToModuleMap: [String: String],
                   topDeclsOnly: Bool,
                   completion: @escaping (String, DeclMap) -> ()) {
        scan(fileToModuleMap) { (path: String, module: String, lock: NSLock?) in
            self.visitSrc(path: path,
                          module: module,
                          topDeclsOnly: topDeclsOnly,
                          whitelist: nil,
                          lock: lock,
                          completion: completion)
        }
    }

    func scanDecls(fileToModuleMap: [String: String],
                   topDeclsOnly: Bool,
                   whitelist: Whitelist?,
                   completion: @escaping (String, DeclMap) -> ()) {
        scan(fileToModuleMap) { (path: String, module: String, lock: NSLock?) in
            self.visitSrc(path: path,
                          module: module,
                          topDeclsOnly: topDeclsOnly,
                          whitelist: whitelist,
                          lock: lock,
                          completion: completion)
        }
    }

    var wpaths = 0
    var npaths = 0
    private func visitSrc(path: String,
                          module: String?,
                          topDeclsOnly: Bool,
                          whitelist: Whitelist?,
                          lock: NSLock?,
                          completion: @escaping (String, DeclMap) -> ()) {
        do {
            let node = try SyntaxParser.parse(path)
            let whitelistPath = FileManager.modifiedWithin(whitelist?.thresholdDays, at: path)
            if whitelistPath {
                wpaths += 1
            }
            npaths += 1
            let visitor = DeclVisitor(path,
                                      module: module,
                                      topDeclsOnly: topDeclsOnly,
                                      whitelistPath: whitelistPath,
                                      whitelist: whitelist)
            visitor.walk(node)
            
            lock?.lock()
            defer {lock?.unlock()}
            completion(path, visitor.declMap)
        } catch {
            fatalError(error.localizedDescription)
        }
    }

    func checkRefs(fileToModuleMap: [String: String],
                   declMap: DeclMap,
                   completion: @escaping (String, Set<String>, [String]) -> ()) {

        scan(fileToModuleMap) { (path: String, module: String, lock: NSLock?) in
            self.referenceSrc(path: path, module: module, declMap: declMap, lock: lock, completion: completion)
        }
    }

    private func referenceSrc(path: String,
                              module: String,
                              declMap: DeclMap,
                              lock: NSLock?,
                              completion: @escaping (String, Set<String>, [String]) -> ()) {
        do {
            let node = try SyntaxParser.parse(path)
            let visitor = RefChecker(path, module: module, declMap: declMap)
            visitor.walk(node)
            
            lock?.lock()
            completion(path, visitor.refs, visitor.imports)
            lock?.unlock()
        } catch {
            fatalError(error.localizedDescription)
        }
    }

    // MARK - input is dirs or filepaths

    func scanDecls(paths: [String],
                   isDirs: Bool,
                   topDeclsOnly: Bool,
                   pathToModules: [String: String],
                   whitelist: Whitelist?,
                   completion: @escaping (String, DeclMap) -> ()) {
        if isDirs {
            scan(dirs: paths) { (path: String, lock: NSLock?) in
                self.visitSrc(path: path,
                              module: pathToModules[path],
                              topDeclsOnly: topDeclsOnly,
                              whitelist: whitelist,
                              lock: lock,
                              completion: completion)
            }
        } else {
            scan(paths) { (path: String, lock: NSLock?) in
                self.visitSrc(path: path,
                              module: pathToModules[path],
                              topDeclsOnly: topDeclsOnly,
                              whitelist: whitelist,
                              lock: lock,
                              completion: completion)
            }
        }
    }


    func checkRefs(paths: [String],
                   isDirs: Bool,
                   pathToModules: [String: String],
                   declMap: DeclMap,
                   completion: @escaping (String, Set<String>, [String]) -> ()) {

        if isDirs {
            scan(dirs: paths) { (path: String, lock: NSLock?) in
                self.referenceSrc(path: path, module: pathToModules[path] ?? "", declMap: declMap, lock: lock, completion: completion)
            }
        } else {
            scan(paths) { (path: String, lock: NSLock?) in
                self.referenceSrc(path: path, module: pathToModules[path] ?? "", declMap: declMap, lock: lock, completion: completion)
            }
        }
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/FileUpdaters/DeclUpdater.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//


import Foundation
import SwiftSyntax
import SwiftSyntaxParser

final class DeclUpdater {

    func updateAccessLevels(filesToDecls: [String: [DeclMetadata]],
                            filesToModules: [String: String],
                            completion: @escaping (String, String) -> ()) {

        scan(filesToDecls) { (path, decls, lock) in
            do {
                let node = try SyntaxParser.parse(path)
                let rewriter = AccessLevelRewriter(path, module: filesToModules[path], decls: decls)
                let ret = rewriter.visit(node)
                lock?.lock()
                completion(path, ret.description)
                lock?.unlock()
            }  catch {
                fatalError(error.localizedDescription)
            }
        }
    }

    func removeDeadDecls(filesToDecls: [String: [DeclMetadata]],
                         completion: @escaping (String, String) -> ()) {
        scan(filesToDecls) { (path, decls, lock) in
            do {
                let node = try SyntaxParser.parse(path)
                let remover = DeclRemover(path, decls: decls)
                let ret = remover.visit(node)

                lock?.lock()
                completion(path, ret.description)
                lock?.unlock()
            }  catch {
                fatalError(error.localizedDescription)
            }
        }
    }

    func removeUnusedImports(paths: [String],
                             isDirs: Bool,
                             unusedImports: [String: [String]],
                             completion: @escaping (String, String) -> ()) {
        if isDirs {
            scan(dirs: paths) { (path, lock) in
                self.updateSrcs(path: path, module: "", lock: lock, unusedImports: unusedImports, completion: completion)
            }
        } else {
            scan(paths) { (path, lock) in
                self.updateSrcs(path: path, module: "", lock: lock, unusedImports: unusedImports, completion: completion)
            }
        }
    }

    func removeUnusedImports(fileToModuleMap: [String: String],
                             unusedImports: [String: [String]],
                             completion: @escaping (String, String) -> ()) {
        scan(fileToModuleMap) { (path, module, lock) in
            self.updateSrcs(path: path, module: module, lock: lock, unusedImports: unusedImports, completion: completion)
        }
    }

    private func updateSrcs(path: String,
                            module: String,
                            lock: NSLock?,
                            unusedImports: [String: [String]],
                            completion: @escaping (String, String) -> ()) {
        do {
            let node = try SyntaxParser.parse(path)
            let remover = ImportRewriter(path, unusedModules: unusedImports[path])
            let ret = remover.visit(node)

            lock?.lock()
            completion(path, ret.description)
            lock?.unlock()
        } catch {
            fatalError(error.localizedDescription)
        }
    }
}



================================================
FILE: Sources/SwiftCodeSanKit/Operations/RemoveDeadDecls.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

public func removeDeadDecls(filesToModules: [String: String],
                            whitelist: Whitelist?,
                            topDeclsOnly: Bool,
                            inplace: Bool,
                            testFiles: [String]?,
                            inplaceTests: Bool,
                            logFilePath: String? = nil,
                            concurrencyLimit: Int? = nil,
                            onCompletion: @escaping () -> ()) {
    
    log("Start of removing dead code: topDeclsOnly", topDeclsOnly)
    scanConcurrencyLimit = concurrencyLimit
    let p = DeclParser()
    var pathToDeclsUpdate = [String: [DeclMetadata]]()
    
    log("Scan and map top-level decls...")
    logTime()
    let declMap = p.scanAndMapDecls(fileToModuleMap: filesToModules,
                                    topDeclsOnly: false,
                                    whitelist: whitelist)
    logTime()
    print("WWW: ", p.npaths, p.wpaths)
    
    log("Check references, look up their source modules, and mark used...")
    let flatDeclMap = flatten(declMap: declMap)
    var nref = 0
    p.checkRefs(fileToModuleMap: filesToModules, declMap: flatDeclMap) { (path, refs, imports) in
        if let refModule = filesToModules[path] {
            markUsed(refs, in: refModule, imports: imports, with: flatDeclMap, updateMembers: true)
        }
        
        log("#Checked refs", counter: &nref, interval: 1000, timed: true)
    }
    logTime()
    
    repeat {
        log("Look up interface members and mark used if any...")
        shouldRetry = false
        markInterfaceMembersUsed(declMap: declMap)
        logTime()
    } while shouldRetry
    var i = flatDeclMap.values.flatMap{$0}.filter{$0.used}.count
    
    log("Mark bound types used...")
    markBoundTypesUsed(declMap: flatDeclMap)
    resetVisited(declMap: declMap)
    var j = flatDeclMap.values.flatMap{$0}.filter{$0.used}.count
    logTime()
    
    while i != j {
        log("#Remaining decls to mark used", j-i, j, i)
        log("Repeat: Look up interface members and mark used if any...")
        markInterfaceMembersUsed(declMap: declMap)
        i = flatDeclMap.values.flatMap{$0}.filter{$0.used}.count
        logTime()
        
        log("Repeat: Mark bound types used...")
        markBoundTypesUsed(declMap: flatDeclMap)
        resetVisited(declMap: flatDeclMap)
        j = flatDeclMap.values.flatMap{$0}.filter{$0.used}.count
        logTime()
    }
    
    log("Filter out used decls...")
    for (_, decls) in flatDeclMap {
        for decl in decls {
            if !decl.used {
                if pathToDeclsUpdate[decl.path] == nil {
                    pathToDeclsUpdate[decl.path] = []
                }
                pathToDeclsUpdate[decl.path]?.append(decl)
            }
        }
    }
    
    let totalUnused = pathToDeclsUpdate.values.flatMap{$0}.count
    let totalUsed = flatDeclMap.values.flatMap{$0}.filter{$0.used}.count
    logTime()
    log("#Total top-level decls: ", declMap.values.flatMap{$0}.count,
        "#Total decls", flatDeclMap.values.flatMap{$0}.count,
        "#Decls Unused", totalUnused,
        "#Decls Used", totalUsed,
        "#Files to update", pathToDeclsUpdate.count)
    
    if let logfile = logFilePath {
        log("Save results to", logfile)
        
        let ret = pathToDeclsUpdate.map { arg in
            let vals = arg.value.map{ ObjectIdentifier($0).debugDescription  + ", " + $0.fullName + ", " + $0.encloser }
            let valStr = vals.joined(separator: "\n")
            return "\(arg.key)\n--- \(valStr)\n"
        }.joined(separator: "\n")
        try? ret.write(toFile: logfile, atomically: true, encoding: .utf8)
        logTime()
    }
    
    if inplace {
        log("Remove unused decls from files...", pathToDeclsUpdate.count)
        let updater = DeclUpdater()
        updater.removeDeadDecls(filesToDecls: pathToDeclsUpdate) { (path, content) in
            try? content.write(toFile: path, atomically: true, encoding: .utf8)
        }
        logTime()
    }
    
    logTotalElapsed("Done")
    
    onCompletion()
}


// MARK - private functions

private func markBoundTypesUsed(declMap: DeclMap) {
    for (k, decls) in declMap {
        if !k.isEmpty {  // Empty means expr or stmt
            for decl in decls {
                if decl.used {
                    decl.visited = true
                    markBoundTypesUsed(decl, level: 0, declMap: declMap)
                }
            }
        }
    }
}

private func markBoundTypesUsed(_ decl: DeclMetadata, level: Int, declMap: DeclMap) {
    
    for boundType in decl.boundTypes {
        if !boundType.isEmpty {
            var bases: [String]?
            var leaf: String?
            if boundType.contains(".") {
                bases = boundType.components(separatedBy: ".")
                leaf = bases?.removeLast()
            }
            
            let key = leaf ?? boundType
            
            if let boundDecls = declMap[key] {
                for boundDecl in boundDecls {
                    if boundDecl.visited, boundDecl.used {
                        continue
                    }
                    boundDecl.visited = true
                    if decl.module == boundDecl.module || decl.imports.contains(boundDecl.module) {
                        boundDecl.used = true
                        markBoundTypesUsed(boundDecl, level: level + 1, declMap: declMap)
                    }
                }
            }
        }
    }
}


var shouldRetry = false
private func markInterfaceMembersUsed(declMap: DeclMap) {
    var ndecls = 0
    scan(declMap) { (key, vals, lock) in
        for cur in vals {
            var members = [DeclMetadata]()
            var interfaceMembers = [DeclMetadata]()
            var userDefinedTypes = [String]()
            var stdlibTypes = [String]()
            let level = 0
            markBoundMembersUsed(key: cur, declMap: declMap, level: level, members: &members, interfaceMembers: &interfaceMembers,  userDefinedTypes: &userDefinedTypes, stdlibTypes: &stdlibTypes)
            log("#Marked used members", counter: &ndecls, interval: 10000, timed: true)
        }
    }
    
}

private func markBoundMembersUsed(key cur: DeclMetadata,
                                  declMap: DeclMap,
                                  level: Int,
                                  members: inout [DeclMetadata],
                                  interfaceMembers: inout [DeclMetadata],
                                  userDefinedTypes: inout [String],
                                  stdlibTypes: inout [String]) {
    
    // First resolve inheritance (loop up protocol conformance, subclassing, and update member ALs)
    var parents = cur.inheritedTypes
    let curIsExtension = cur.declType == .extensionType
    if curIsExtension {
        parents.append(cur.name)
    }
    resolveInheritance(key: cur, inheritedTypes: parents, declMap: declMap, level: level, members: &members, interfaceMembers: &interfaceMembers, userDefinedTypes: &userDefinedTypes, stdlibTypes: &stdlibTypes)
    
    let stdTypes = stdlibTypes.filter{!userDefinedTypes.contains($0)}
    
    for member in members {
        let matchingMembers = interfaceMembers.filter {$0.name == member.name}
        for matched in matchingMembers {
            if matched.used {
                member.used = true
            } else if member.used || member.isOverride {
                if !matched.used {
                    matched.used = true
                    shouldRetry = true
                }
            } else if !stdTypes.isEmpty {
                if !matched.used {
                    matched.used = true
                    shouldRetry = true
                }
                member.used = true
            }
        }
        
        if matchingMembers.isEmpty, member.isOverride {
            // This might be a member overriding stdlib api
            member.used = true
        }
    }
    
    // For the following decl types, check bound types and update member ALs.
    if cur.declType == .extensionType || cur.declType == .enumType {
        if !cur.used {
            for m in cur.members {
                if m.used {
                    cur.used = true
                    break
                }
            }
        }
        
        if !cur.used {
            let boundTypes = cur.boundTypes.filter{!cur.inheritedTypes.contains($0)}
            for boundType in boundTypes {
                if boundType.isEmpty {
                    continue
                }
                if cur.name != boundType, let _ = declMap[boundType] {
                    // even if boundtype is used, cur might not be used
                } else if cur.inheritedTypes.contains(boundType) {
                    // If parent is not in declMap, assume it's in stdlib.
                    for member in cur.members {
                        member.used = true
                        cur.used = true
                    }
                    if cur.used {
                        break
                    }
                }
            }
        }
    }
}

private func resolveInheritance(key cur: DeclMetadata,
                                inheritedTypes: [String]?,
                                declMap: DeclMap,
                                level: Int,
                                members: inout [DeclMetadata],
                                interfaceMembers: inout [DeclMetadata],
                                userDefinedTypes: inout [String],
                                stdlibTypes: inout [String]) {
    
    let parents = inheritedTypes ?? cur.inheritedTypes
    
    for parent in parents {
        if parent.isEmpty {
            continue
        }
        if let parentDecls = declMap[parent] {
            for parentDecl in parentDecls {
                if parentDecl.name.isEmpty {
                    continue
                }
                if parentDecl.declType == .protocolType || parentDecl.declType == .classType || parentDecl.declType == .typealiasType {
                    if parentDecl.declType == .protocolType {
                        interfaceMembers.append(contentsOf: parentDecl.members)
                    } else if parentDecl.declType == .classType, cur.declType == .classType {
                        interfaceMembers.append(contentsOf: parentDecl.members)
                    }
                    
                    userDefinedTypes.append(parentDecl.name)
                    members.append(contentsOf: cur.members)
                    
                    let optionalInitialTypes = parentDecl.declType == .typealiasType ? parentDecl.boundTypes : nil
                    
                    resolveInheritance(key: parentDecl, inheritedTypes: optionalInitialTypes, declMap: declMap, level: level+1, members: &members,  interfaceMembers: &interfaceMembers, userDefinedTypes: &userDefinedTypes, stdlibTypes: &stdlibTypes)
                    
                } else if parentDecl.declType == .extensionType {
                    // Parent could be a user defined type or a stdlib type. Add to a list for now and filter out below.
                    stdlibTypes.append(parentDecl.name)
                    members.append(contentsOf: cur.members)
                }
            }
        } else {
            // If parent is not in declMap, assume it's in stdlib.
            stdlibTypes.append(parent)
        }
    }
    
    for stdlibType in stdlibTypes {
        if userDefinedTypes.contains(stdlibType) {
            continue
        }
        
        interfaceMembers.append(contentsOf: cur.members)
        members.append(contentsOf: cur.members)
        break
    }
}

private func accessMembers(_ bases: [String], _ i: Int, _ refModule: String,  _ imports: [String], declMap: DeclMap) -> Bool {
    let j = i + 1
    
    if j < bases.count {
        let cur = bases[i]
        let next = bases[j]
        if let prefixDecls = declMap[cur] {
            for prefixDecl in prefixDecls {
                var list: [DeclMetadata]?
                
                if prefixDecl.declType == .funcType ||
                    prefixDecl.declType == .operatorType ||  // This is handled here but shouldn't be member-accessed
                    prefixDecl.declType == .varType {
                    if let typeDecls = declMap[prefixDecl.type] {
                        for t in typeDecls {
                            list = t.members.filter{$0.name == next}
                        }
                    }
                    
                } else {
                    list = prefixDecl.members.filter{$0.name == next}
                }
                
                if let list = list, !list.isEmpty {
                    
                    let accessed = accessMembers(bases, i + 1, refModule, imports, declMap: declMap)
                    
                    if accessed, (refModule == prefixDecl.module || imports.contains(prefixDecl.module)) {
                        for member in list {
                            member.used = true
                        }
                    }
                    
                } else {
                    return false
                }
            }
        }
    }
    return true
}

private func markUsed(_ refs: Set<String>, in refModule: String, imports: [String], with declMap: DeclMap, updateMembers: Bool) {
    // Leaf level node checks
    for r in refs {
        var bases: [String]?
        var leaf: String?
        
        if r.contains(".") {
            bases = r.components(separatedBy: ".")
        }
        
        // First, traverse member access, and update visibility along the way
        var accessedMembers = false
        if let bases = bases {
            accessedMembers = accessMembers(bases, 0, refModule, imports, declMap: declMap)
        }
        if accessedMembers {
            continue
        }
        
        leaf = bases?.removeLast()
        let refKey = leaf ?? r
        
        // If above fails (e.g. encloser type is not found), or non-member access, try following
        if let refDecls = declMap[refKey] {
            for refDecl in refDecls {
                if refModule == refDecl.module || imports.contains(refDecl.module) || refDecl.isOverride {
                    refDecl.used = true
                }
            }
            
        }
    }
}

private func resetVisited(declMap: DeclMap) {
    for (_, decls) in declMap {
        for decl in decls {
            decl.visited = false
        }
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Operations/RemoveUnusedImports.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

public func removeUnusedImports(fileToModuleMap: [String: String],
                                whitelist: Whitelist?,
                                topDeclsOnly: Bool,
                                inplace: Bool,
                                logFilePath: String? = nil,
                                concurrencyLimit: Int? = nil) {
    scanConcurrencyLimit = concurrencyLimit

    let p = DeclParser()
    
    log("Scan all decls and generate a decl map...")
    logTime()

    let allDeclMap = p.scanAndMapDecls(fileToModuleMap: fileToModuleMap,
                                       topDeclsOnly: topDeclsOnly)

    logTime()
    log("#Decls", allDeclMap.keys.count)

    var unusedImports = [String: [String]]()

    let whitelistModulesBlock = { (module: String) -> Bool in
        let moduleComps = module.components(separatedBy: ".").filter {!$0.isEmpty}
        for comp in moduleComps {
            if let list = whitelist?.modules, list.contains(comp) {
                return true
            }
            if let list = whitelist?.modulesSuffix {
                for suffix in list {
                    if comp.hasSuffix(suffix) {
                        return true
                    }
                }
            }
            if let list = whitelist?.modulesPrefix {
                for prefix in list {
                    if comp.hasPrefix(prefix) {
                        return true
                    }
                }
            }
        }
        return false
    }

    log("Check referenced decls and compare their source modules against imported modules to filter out unused imports...")
    var total = 0
    p.checkRefs(fileToModuleMap: fileToModuleMap, declMap: allDeclMap) { (filepath, refs, imports) in
        var usedImportsInFile = [String: Bool]()
        for i in imports {
            usedImportsInFile[i] = whitelistModulesBlock(i)
        }
        for r in refs {
            if let refDecls = allDeclMap[r] {
                for refDecl in refDecls {
                    let m = refDecl.module

                    if imports.contains(m) {
                        usedImportsInFile[m] = true
                    } else {
                        let refinedImports = imports.filter {$0.contains(".")}
                        for item in refinedImports {
                            let comps = item.components(separatedBy: ".")
                            if comps.contains(m) {
                                usedImportsInFile[item] = true
                            }
                        }
                    }
                }
            } else if imports.contains(r) {
                // Sometimes a module name can be used in code, e.g. CoreFoundation.Foo
                usedImportsInFile[r] = true
            }
        }

        var unusedListInFile = [String]()
        for (module, used) in usedImportsInFile {
            if !used {
                total += 1
                unusedListInFile.append(module)
            }
        }

        if !unusedListInFile.isEmpty {
            unusedImports[filepath] = Set(unusedListInFile).compactMap{$0}
        }

//        log(total, interval: 200)
    }

    logTime()
    log("#Unused imports", total)

    if let op = logFilePath {
        log("Save results...")

        var totalUnused = 0
        var ret = unusedImports.map { (path, unusedlist) -> String in
            totalUnused += unusedlist.count
            return path + "\n" + String(unusedlist.count) + "\n" + unusedlist.joined(separator: ", ")
        }
        assert(total == totalUnused)
        ret.append("Total unused: \(totalUnused)")
        let retStr = ret.joined(separator: "\n\n")

        let declstr = allDeclMap.map{ (k, v) -> String in
            let t = """
            \(k):  \(v.map { $0.path }.joined(separator: ", "))
            """
            return t
        }.joined(separator: "\n")

        try? retStr.write(toFile: op, atomically: true, encoding: .utf8)
        try? declstr.write(toFile: op+"-decls", atomically: true, encoding: .utf8)
    }

    if inplace {
        log("Remove unused imports from files...", unusedImports.keys.count)
        let updater = DeclUpdater()
        updater.removeUnusedImports(fileToModuleMap: fileToModuleMap,
                                    unusedImports: unusedImports) { (path, result) in
                                        try? result.write(toFile: path, atomically: true, encoding: .utf8)
        }
    }

    logTime()

    logTotalElapsed("Done")
}


================================================
FILE: Sources/SwiftCodeSanKit/Operations/UpdateAccessLevels.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

public func updateAccessLevels(filesToModules: [String: String],
                               whitelist: Whitelist?,
                               inplace: Bool,
                               logFilePath: String? = nil,
                               concurrencyLimit: Int? = nil,
                               onCompletion: @escaping () -> ()) {
    
    scanConcurrencyLimit = concurrencyLimit
    let p = DeclParser()
    var pathToDeclsUpdate = [String: [DeclMetadata]]()
    
    log("Scan and map top-level decls...")
    logTime()
    let declMap = p.scanAndMapDecls(fileToModuleMap: filesToModules,
                                    topDeclsOnly: false,
                                    whitelist: whitelist)
    
    logTime()
    
    log("Check references, look up their source modules, and mark visibility...")
    p.checkRefs(fileToModuleMap: filesToModules, declMap: declMap) { (path, refs, imports) in
        if let refModule = filesToModules[path] {
            markVisiblity(refs, in: refModule, imports: imports, with: declMap, updateMembers: true)
        }
    }
    
    logTime()
    
    log("Update ALs (access levels) of top-level decls and their bound types...")
    updateBoundTypeALs(declMap: declMap)
    resetVisited(declMap: declMap)
    
    log("Update ALs of member decls of interfaces (protocol / base class)...")
    updateMemberALs(declMap: declMap)
    
    logTime()
    
    log("Flatten decls, and check references again, for member decls...")
    var nref = 0
    let flatDeclMap = flatten(declMap: declMap)
    p.checkRefs(fileToModuleMap: filesToModules, declMap: flatDeclMap) { (path, refs, imports) in
        if let refModule = filesToModules[path] {
            markVisiblity(refs, in: refModule, imports: imports, with: flatDeclMap, updateMembers: false)
        }
        log(counter: &nref, interval: 1000)
    }
    log(nref)
    
    logTime()
    
    log("Update ALs of all decls and their bound types...")
    updateBoundTypeALs(declMap: flatDeclMap)
    resetVisited(declMap: flatDeclMap)
    
    var i = -1
    var j = 0
    
    while i != j {
        log("If bound types are modified, update their member ALs as well...")
        updateMemberALs(declMap: declMap)
        i = flatDeclMap.values.flatMap{$0}.filter{$0.shouldExpose}.count
        
        log("Again, update ALs of of all decls and their bound types...")
        updateBoundTypeALs(declMap: flatDeclMap)
        resetVisited(declMap: flatDeclMap)
        j = flatDeclMap.values.flatMap{$0}.filter{$0.shouldExpose}.count
        
        log("#Remaining decls to update", i-j, i, j)
    }
    
    log("Save decls to update per files...")
    for (_, decls) in flatDeclMap {
        for decl in decls {
            if decl.isPublicOrOpen, !decl.shouldExpose {
                if pathToDeclsUpdate[decl.path] == nil {
                    pathToDeclsUpdate[decl.path] = []
                }
                pathToDeclsUpdate[decl.path]?.append(decl)
            }
        }
    }
    
    if let logfile = logFilePath {
        log("Save results to", logfile)
        let ret = pathToDeclsUpdate.map {"\($0.key): \($0.value.map{$0.name + ", " + $0.encloser}.joined(separator: "\n"))"}.joined(separator: "\n")
        try? ret.write(toFile: logfile, atomically: true, encoding: .utf8)
    }
    
    if inplace {
        log("Update decl ALs in files...")
        let updater  = DeclUpdater()
        updater.updateAccessLevels(filesToDecls: pathToDeclsUpdate, filesToModules: filesToModules) { (path, content) in
            try? content.write(toFile: path, atomically: true, encoding: .utf8)
        }
    }
    logTime()
    
    let total = pathToDeclsUpdate.values.flatMap{$0}.count
    log("#Total top-level decls: ", declMap.count, "#Total decls", flatDeclMap.count, "#Decls updated", total, "#Files updated", pathToDeclsUpdate.count)
    logTotalElapsed("Done")
    
    onCompletion()
}


// MARK - private functions

private func updateBoundTypeALs(declMap: DeclMap) {
    for (k, decls) in declMap {
        if !k.isEmpty {  // Empty means expr or stmt
            for decl in decls {
                if (decl.isPublicOrOpen && decl.shouldExpose) ||
                    decl.declType == .extensionType ||
                    decl.isExtensionMember {
                    decl.visited = true
                    updateBoundTypeALs(decl, level: 0, declMap: declMap)
                }
            }
        }
    }
}

private func updateBoundTypeALs(_ decl: DeclMetadata, level: Int, declMap: DeclMap) {
    
    for boundType in decl.boundTypesAL {
        if !boundType.isEmpty {
            var bases: [String]?
            var leaf: String?
            if boundType.contains(".") {
                bases = boundType.components(separatedBy: ".")
                leaf = bases?.removeLast()
            }
            
            let key = leaf ?? boundType
            
            if let boundDecls = declMap[key] {
                for boundDecl in boundDecls {
                    if boundDecl.visited, boundDecl.shouldExpose {
                        continue
                    }
                    boundDecl.visited = true
                    if decl.module == boundDecl.module || decl.imports.contains(boundDecl.module) {
                        boundDecl.shouldExpose = true
                        updateBoundTypeALs(boundDecl, level: level + 1, declMap: declMap)
                    }
                }
            }
        }
    }
}

private func updateMemberALs(declMap: DeclMap) {
    for (_, vals) in declMap {
        for cur in vals {
            var members = [DeclMetadata]()
            var interfaceMembers = [DeclMetadata]()
            let level = 0
            
            updateBoundMemberALs(key: cur, declMap: declMap, level: level, members: &members, interfaceMembers: &interfaceMembers)
        }
    }
}

private func updateBoundMemberALs(key cur: DeclMetadata,
                                  declMap: DeclMap,
                                  level: Int,
                                  members: inout [DeclMetadata],
                                  interfaceMembers: inout [DeclMetadata]) {
    
    // First resolve inheritance (loop up protocol conformance, subclassing, and update member ALs)
    var parents = cur.inheritedTypes
    let curIsExtension = cur.declType == .extensionType
    if curIsExtension {
        parents.append(cur.name)
    }
    resolveInheritance(key: cur, inheritedTypes: parents, declMap: declMap, level: level, members: &members, interfaceMembers: &interfaceMembers)
    
    let interfaceMemberNames = interfaceMembers.map{$0.name}
    for member in members {
        if interfaceMemberNames.contains(member.name) {
            if member.isPublicOrOpen || (curIsExtension && cur.isPublicOrOpen) {
                member.shouldExpose = true
                
                // If encloser is extension, it should be also exposed since its member is public/exposed
                if curIsExtension, !cur.shouldExpose {
                    cur.shouldExpose = true
                }
            }
        } else if member.isPublicOrOpen, member.isOverride {
            // This might be a member overriding stdlib api
            member.shouldExpose = true
        }
    }
    
    // For the following decl types, check bound types and update member ALs.
    if cur.declType == .extensionType || cur.declType == .enumType {
        
        var visitedCurrent = false
        let boundTypesAL = cur.boundTypesAL.filter{!cur.inheritedTypes.contains($0)}
        
        for boundType in boundTypesAL {
            if !boundType.isEmpty, cur.name != boundType, let boundTypeVals = declMap[boundType] {
                for boundDecl in boundTypeVals {
                    if !visitedCurrent,
                       boundDecl.isPublicOrOpen,
                       boundDecl.shouldExpose {
                        
                        for member in cur.members {
                            if member.isPublicOrOpen {
                                member.shouldExpose = true
                            }
                        }
                        visitedCurrent = true
                    }
                }
            } else if !visitedCurrent, cur.inheritedTypes.contains(boundType) {
                // If parent is not in declMap, assume it's in stdlib.
                for member in cur.members {
                    if member.isPublicOrOpen {
                        member.shouldExpose = true
                    }
                }
                visitedCurrent = true
            }
        }
        
        if visitedCurrent, !cur.shouldExpose {
            cur.shouldExpose = true
        }
    }
}


private func resolveInheritance(key cur: DeclMetadata,
                                inheritedTypes: [String]?,
                                declMap: DeclMap,
                                level: Int,
                                members: inout [DeclMetadata],
                                interfaceMembers: inout [DeclMetadata]) {
    
    let parents = inheritedTypes ?? cur.inheritedTypes
    var stdlibTypes = [String]()
    var userDefinedTypes = [String]()
    
    for parent in parents {
        if parent.isEmpty {
            continue
        }
        if let parentDecls = declMap[parent] {
            for parentDecl in parentDecls {
                if parentDecl.name.isEmpty {
                    continue
                }
                if parentDecl.declType == .protocolType || parentDecl.declType == .classType || parentDecl.declType == .typealiasType {
                    if parentDecl.isPublicOrOpen, parentDecl.shouldExpose {
                        if parentDecl.declType == .protocolType {
                            interfaceMembers.append(contentsOf: parentDecl.members)
                        } else if parentDecl.declType == .classType, cur.declType == .classType {
                            interfaceMembers.append(contentsOf: parentDecl.members)
                        }
                    }
                    
                    userDefinedTypes.append(parentDecl.name)
                    members.append(contentsOf: cur.members)
                    
                    let optionalInitialTypes = parentDecl.declType == .typealiasType ? parentDecl.boundTypesAL : nil
                    
                    resolveInheritance(key: parentDecl, inheritedTypes: optionalInitialTypes, declMap: declMap, level: level+1, members: &members,  interfaceMembers: &interfaceMembers)
                    
                } else if parentDecl.declType == .extensionType {
                    // Parent could be a user defined type or a stdlib type. Add to a list for now and filter out below.
                    stdlibTypes.append(parentDecl.name)
                }
            }
        } else {
            // If parent is not in declMap, assume it's in stdlib.
            stdlibTypes.append(parent)
        }
    }
    
    for stdlibType in stdlibTypes {
        if userDefinedTypes.contains(stdlibType) {
            continue
        }
        
        for member in cur.members {
            if member.isPublicOrOpen {
                interfaceMembers.append(member)
                members.append(member)
            }
        }
        break
    }
}

private func traverseMembers(_ bases: [String], _ i: Int, _ refModule: String,  _ imports: [String], declMap: DeclMap) -> Bool {
    let j = i + 1
    
    if j < bases.count {
        let cur = bases[i]
        let next = bases[j]
        if let prefixDecls = declMap[cur] {
            for prefixDecl in prefixDecls {
                var list: [DeclMetadata]?
                if prefixDecl.declType == .funcType ||
                    prefixDecl.declType == .operatorType ||  // This is handled here but shouldn't be member-accessed
                    prefixDecl.declType == .varType {
                    if let typeDecls = declMap[prefixDecl.type] {
                        for t in typeDecls {
                            list = t.members.filter{$0.name == next}
                        }
                    }
                } else {
                    list = prefixDecl.members.filter{$0.name == next}
                }
                
                if let list = list, !list.isEmpty {
                    let checked = traverseMembers(bases, i + 1, refModule, imports, declMap: declMap)
                    if checked, refModule != prefixDecl.module, imports.contains(prefixDecl.module) {
                        for member in list {
                            member.shouldExpose = true
                        }
                    }
                    
                } else {
                    return false
                }
            }
        }
    }
    return true
}

private func markVisiblity(_ refs: Set<String>, in refModule: String, imports: [String], with declMap: DeclMap, updateMembers: Bool) {
    // Leaf level node checks
    for r in refs {
        
        var bases: [String]?
        var leaf: String?
        
        if r.contains(".") {
            bases = r.components(separatedBy: ".")
        }
        
        // First, traverse member access, and update visibility along the way
        var accessedMembers = false
        if let bases = bases {
            accessedMembers = traverseMembers(bases, 0, refModule, imports, declMap: declMap)
        }
        if accessedMembers {
            continue
        }
        
        leaf = bases?.removeLast()
        let refKey = leaf ?? r
        
        // If above fails (e.g. encloser type is not found), or non-member access, try following
        if let refDecls = declMap[refKey] {
            for refDecl in refDecls {
                if true ||
                    refDecl.isPublicOrOpen ||
                    refDecl.declType == .extensionType ||
                    refDecl.isExtensionMember {
                    
                    // multi modules w/ same decls (foo):
                    // 1. shadowing: if ref'd, it uses a decl in the same module even if the others are imported.
                    //      - if foo from another module should be called, it's required to use qualifier X.foo
                    // 2. if not decl's in the same module as ref, uses corresponding modules, so need to look up imports
                    // 3. if foo inits are the same for multi-modules:
                    //     - need qualifier X.foo
                    if refModule == refDecl.module {
                        // r is either declared internally
                        // so r should not be public, so add [decl.path: r] to pathToUpdateDecls
                    } else {
                        // look up imports and check decl.module is in the imports, then decl.shouldBePublic = true, so do nothing.
                        if imports.contains(refDecl.module) {
                            
                            if !refDecl.encloser.isEmpty {
                                // If it has an encloser (part of a class, protocol, etc),
                                // check if the encloser is in ref'd.
                                // Encloser type might not be listed, leakdetect.inst.accumulatedLeaksStream
                                refDecl.shouldExpose = true
                            } else {
                                // then r in decl.module should remain public
                                refDecl.shouldExpose = true
                            }
                        } else {
                            // r must be part of stdlib, handled in updateMemberALs above.
                        }
                    }
                    
                }
            }
        }
    }
}


private func shouldMatchACLForMembers(_ declType: DeclType) -> Bool {
    return declType == .protocolType ||
        declType == .extensionType ||
        declType == .enumType
}

private func resetVisited(declMap: DeclMap) {
    for (_, decls) in declMap {
        for decl in decls {
            decl.visited = false
        }
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Utils/Extensions/FileManagerExtensions.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

extension FileManager {

    static func modifiedWithin(_ delta: Int?, at path: String) -> Bool {
        if let fileAttrs = try? FileManager.default.attributesOfItem(atPath: path),
            let modifiedDate = fileAttrs[FileAttributeKey.creationDate] as? Date {
            let now = Date()
            let days = Int(floor(modifiedDate.distance(to: now) / 60 / 60 / 24))
            if let delta = delta, days < delta {
                return true
            }
        }
        return false
    }
}



================================================
FILE: Sources/SwiftCodeSanKit/Utils/Extensions/SequenceExtensions.swift
================================================

//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

public extension Sequence {
    
    func compactMap<T>(path: KeyPath<Element, T?>) -> [T] {
        return compactMap { (element) -> T? in
            element[keyPath: path]
        }
    }
    func map<T>(path: KeyPath<Element, T>) -> [T] {
        return map { (element) -> T in
            element[keyPath: path]
        }
    }
    
    func filter(path: KeyPath<Element, Bool>) -> [Element] {
        return filter { (element) -> Bool in
            element[keyPath: path]
        }
    }
    
    func sorted<T>(path: KeyPath<Element, T>) -> [Element] where T: Comparable {
        return sorted { (lhs, rhs) -> Bool in
            lhs[keyPath: path] < rhs[keyPath: path]
        }
    }
    
    func sorted<T, U>(path: KeyPath<Element, T>, fallback: KeyPath<Element, U>) -> [Element] where T: Comparable, U: Comparable {
        return sorted { (lhs, rhs) -> Bool in
            if lhs[keyPath: path] == rhs[keyPath: path] {
                return lhs[keyPath: fallback] < rhs[keyPath: fallback]
            }
            
            return lhs[keyPath: path] < rhs[keyPath: path]
        }
    }
}




================================================
FILE: Sources/SwiftCodeSanKit/Utils/Extensions/StringExtensions.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

var alphanumericSet = CharacterSet.alphanumerics

extension String {
    static public let `final` = "final"
    static let override = "override"
    static let unknownVal = "Unknown"
    static let prefix = "prefix"
    static let `public` = "public"
    static let `open` = "open"
    static let `internal` = "internal"
    static let `required` = "required"
    static let `convenience` = "convenience"
    static let moduleColon = "module:"
    static let typealiasColon = "typealias:"
    static let rxColon = "rx:"
    static let varColon = "var:"
    static let annotationArgDelimiter = ";"
    static let transparent = "@_transparent"
    static let propertyWrapper = "propertyWrapper"
    
    var raw: String {
        if hasPrefix("`"), hasSuffix("`") {
            var val = dropFirst()
            val = val.dropLast()
            return String(val)
        }
        return self
    }
    
    func arguments(with delimiter: String) -> [String: String]? {
        let argstr = self
        let args = argstr.components(separatedBy: delimiter)
        var argsMap = [String: String]()
        for item in args {
            let keyVal = item.components(separatedBy: "=").map{$0.trimmed}
            
            if let k = keyVal.first {
                if k.contains(":") {
                    break
                }
                
                if let v = keyVal.last {
                    argsMap[k] = v
                }
            }
        }
        return !argsMap.isEmpty ? argsMap : nil
    }
    
    public var trimmed: String {
        return self.trimmingCharacters(in: .whitespaces)
    }
    
    var isAlphanumeric: Bool {
        let ret = self.unicodeScalars.filter {alphanumericSet.contains($0) || $0 == "_"}
        return !ret.isEmpty
    }
    
    var isPublicOrOpen: Bool {
        return self == .public || self == .open
    }
}



================================================
FILE: Sources/SwiftCodeSanKit/Utils/Extensions/SyntaxExtensions.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//


import Foundation
import SwiftSyntax


protocol DeclProtocol {
    var type: String { get }
    var name: String { get }
    var fullName: String { get }
    var declType: DeclType { get }
    var inheritedTypes: [String] { get }
    func refTypes(with declMap: DeclMap, filterKey: String?) -> [String]
    var refTypes: [String] { get }
    var boundTypes: [String] { get }
    var boundTypesAL: [String] { get } // Bound types for access levels
    var accessLevel: String { get }
    var isOverride: Bool { get }
    var isExprOrStmt: Bool { get }
    func declMetadatas(path: String, module: String, encloser: String, description: String, imports: [String]) -> [DeclMetadata]
}

extension DeclProtocol {
    func declMetadatas(path: String, module: String, encloser: String, description: String, imports: [String]) -> [DeclMetadata] {
        if let declSyntax = self as? DeclSyntax, let varSyntax = declSyntax.as(VariableDeclSyntax.self) {
            return varSyntax.declMetadatas(path: path, module: module, encloser: encloser, description: description, imports: imports)
        }

        let val = DeclMetadata(path: path,
                                 module: module,
                                 imports: imports,
                                 encloser: encloser,
                                 name: name,
                                 type: type,
                                 fullName: fullName,
                                 description: description,
                                 declType: declType,
                                 inheritedTypes: inheritedTypes,
                                 boundTypes: boundTypes,
                                 boundTypesAL: boundTypesAL,
                                 isPublicOrOpen: accessLevel.isPublicOrOpen,
                                 isOverride: isOverride,
                                 used: false)
          return [val]
      }

    func refTypes(with declMap: DeclMap, filterKey: String? = nil) -> [String] {
        return refTypes.filter { declMap[$0] != nil || $0.contains(".") || $0.hasSuffix("Strings") || $0.hasSuffix("Images") }
    }

}

extension Syntax: DeclProtocol {

    var name: String {
        return ""
    }

    var type: String {
        return ""
    }

    var fullName: String {
        return name
    }

    var accessLevel: String {
        return ""
    }

    var isOverride: Bool {
        return false
    }

    var isExprOrStmt: Bool {
        return false
    }

    var declType: DeclType {
        return .other
    }

    var inheritedTypes: [String] {
        return []
    }

    var refTypes: [String] {
        return boundTypesAL
    }

    var boundTypes: [String] {
        return tokens.exprTokenList
    }

    var boundTypesAL: [String] {
        return boundTypes
    }
}

extension DeclSyntax: DeclProtocol {
    var name: String {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.name
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.name
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.name
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.name
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.name
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.name
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.name
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.name
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.name
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.name
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.name
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.name
        } else {
            return ""
        }
    }

    var type: String {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.type
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.type
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.type
        }
        return name
    }

    var fullName: String {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.fullName
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.fullName
        }
        return name
    }


    var declType: DeclType {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.declType
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.declType
        }
        return .other
    }


    var inheritedTypes: [String] {
        if let d = self.as(ProtocolDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.inheritedTypes
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.inheritedTypes
        }
        return []
    }

    func refTypes(with declMap: DeclMap, filterKey: String?) -> [String] {
        var list = refTypes
        if !declMap.isEmpty {
            list = list.filter{declMap[$0] != nil}
        }
        return list
    }

    var refTypes: [String] {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.refTypes
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.refTypes
        }
        return []
    }

    var boundTypes: [String] {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.boundTypes
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.boundTypes
        }
        return []
    }

    var boundTypesAL: [String] {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.boundTypesAL
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.boundTypesAL
        }
        return []
    }

    var accessLevel: String {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(SubscriptDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(ProtocolDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(ClassDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(ExtensionDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(StructDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(EnumDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(EnumCaseDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(TypealiasDeclSyntax.self) {
            return d.accessLevel
        } else if let d = self.as(AssociatedtypeDeclSyntax.self) {
            return d.accessLevel
        }
        return ""
    }

    var isOverride: Bool {
        if let d = self.as(FunctionDeclSyntax.self) {
            return d.isOverride
        } else if let d = self.as(VariableDeclSyntax.self) {
            return d.isOverride
        } else if let d = self.as(InitializerDeclSyntax.self) {
            return d.isOverride
        }
        return false
    }

    var isExprOrStmt: Bool {
        _syntaxNode.is(StmtSyntax.self) || _syntaxNode.is(ExprSyntax.self)
    }
}


extension MemberDeclListItemSyntax: DeclProtocol {
    var refTypes: [String] {
        return decl.refTypes
    }

    var declType: DeclType {
        return decl.declType
    }

    var inheritedTypes: [String] {
        return decl.inheritedTypes
    }

    var boundTypes: [String] {
        return decl.boundTypes
    }

    var boundTypesAL: [String] {
        return decl.boundTypesAL
    }

    var accessLevel: String {
        return decl.accessLevel
    }

    var isOverride: Bool {
        return decl.isOverride
    }

    var isExprOrStmt: Bool {
        return decl.isExprOrStmt
    }

    var name: String {
        return decl.name
    }

    var type: String {
        return decl.type
    }

    var fullName: String {
        return decl.fullName
    }
}

extension MemberDeclListSyntax: DeclProtocol {

    var name: String {
        return ""
    }

    var type: String {
        return name
    }

    var fullName: String {
        return name
    }

    var declType: DeclType {
        return .other
    }

    var inheritedTypes: [String] {
        return []
    }

    var accessLevel: String {
        return ""
    }

    var isOverride: Bool {
        return false
    }

    var isExprOrStmt: Bool {
        return false
    }

    var boundTypes: [String] {
        return self.map { $0.decl.boundTypes }.flatMap{$0}
    }

    var boundTypesAL: [String] {
        return self.map { $0.decl.boundTypesAL }.flatMap{$0}
    }

    var refTypes: [String] {
        return boundTypesAL
    }

}

extension ProtocolDeclSyntax: DeclProtocol {
    var fullName: String {
        return name
    }
    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var name: String {
        return identifier.text.raw
    }
    
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return [inheritanceClause?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter{$0 != name}
    }

    var boundTypes: [String] {
        return inheritedTypes
    }

    var boundTypesAL: [String] {
        return [boundTypes,
                members.members.boundTypesAL,
            ].compactMap{$0}.flatMap{$0}
    }

    var refTypes: [String] {
        return boundTypesAL
    }

    var declType: DeclType {
        return .protocolType
    }
    
    var isPrivate: Bool {
        return self.modifiers?.isPrivate ?? false
    }
    

    var attributesDescription: String {
        self.attributes?.trimmedDescription ?? ""
    }
    
    var offset: Int64 {
        return Int64(self.position.utf8Offset)
    }
    
    func annotationMetadata(with annotation: String) -> AnnotationMetadata? {
        return leadingTrivia?.annotationMetadata(with: annotation)
    }

}

extension ClassDeclSyntax: DeclProtocol {
    var fullName: String {
        return name
    }
    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }

    var isOverride: Bool {
        return false
    }

    var name: String {
        return identifier.text.raw
    }
    
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }
    
    var declType: DeclType {
        return .classType
    }
    
    var boundTypes: [String] {
        return inheritedTypes
    }

    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return [boundTypesAL,
                members.members.boundTypesAL,
            ].compactMap{$0}.flatMap{$0}
    }


    var inheritedTypes: [String] {
        return [genericParameterClause?.genericParameterList.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
                inheritanceClause?.tokens.exprTokenList
            ].compactMap{$0}.flatMap{$0}.filter{$0 != name}
    }

    var attributesDescription: String {
        self.attributes?.trimmedDescription ?? ""
    }
    
    var offset: Int64 {
        return Int64(self.position.utf8Offset)
    }
    
    func annotationMetadata(with annotation: String) -> AnnotationMetadata? {
        return leadingTrivia?.annotationMetadata(with: annotation)
    }
}

extension ExtensionDeclSyntax: DeclProtocol {
    var fullName: String {
        return extendedType.description.trimmed + "_" + inheritedTypes.joined()
    }
    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var name: String {
        let str = extendedType.description.trimmed.raw
        let comps = str.components(separatedBy: ".")
        if let last = comps.last, !last.isEmpty {
            return last
        }
        return str
    }

    var declType: DeclType {
        return .extensionType
    }
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return [inheritanceClause?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter{$0 != name}
    }

    var boundTypes: [String] {
        var ret = inheritedTypes
        ret.append(name)
        return ret
    }

    var boundTypesAL: [String] {
        return [boundTypes,
                members.members.boundTypesAL,
            ].compactMap{$0}.flatMap{$0}
    }


    var refTypes: [String] {
        return boundTypesAL
    }

    func refTypes(with declMap: DeclMap, filterKey: String? = nil) -> [String] {
        var list = [extendedType.tokens.exprTokenList,
                    refTypes
            ].compactMap{$0}.flatMap{$0}

        if !declMap.isEmpty {
            list = list.filter{declMap[$0] != nil}
        }

        return list
    }

}

extension EnumCaseDeclSyntax: DeclProtocol {
    var inheritedTypes: [String] {
        return []
    }

    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var name: String {
        let ret = elements.map{$0.identifier.text}.first ?? elements.description
        return ret.raw
    }

    var fullName: String {
        return self.elements.description
    }

    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var declType: DeclType {
        return .enumCaseType
    }

    var boundTypes: [String] {
        let list = elements.compactMap{$0.associatedValue?.parameterList.compactMap{$0.type?.tokens.exprTokenList}.flatMap{$0}}.flatMap{$0}
        return list
    }

    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return boundTypesAL
    }
}

extension EnumDeclSyntax: DeclProtocol {
    var fullName: String {
        return name + "_" + inheritedTypes.joined()
    }
    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var attributesDescription: String {
        self.attributes?.description.trimmed ?? ""
    }

    var name: String {
        return identifier.text.trimmed.raw
    }
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var declType: DeclType {
        return .enumType
    }

    var inheritedTypes: [String] {
        return [inheritanceClause?.tokens.exprTokenList,
                genericParameters?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList
            ].compactMap{$0}.flatMap{$0}.filter{ $0 != name }
    }

    var boundTypes: [String] {
        return inheritedTypes
    }

    var boundTypesAL: [String] {
        return [boundTypes,
                members.members.boundTypesAL
            ].compactMap{$0}.flatMap{$0}
    }

    var refTypes: [String] {
        return boundTypesAL
    }
}

extension StructDeclSyntax: DeclProtocol {
    var fullName: String {
        return name + "_" + inheritedTypes.joined()
    }
    var type: String {
        return name
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var attributesDescription: String {
        self.attributes?.description.trimmed ?? ""
    }

    var name: String {
        return identifier.text.trimmed.raw
    }
    var declType: DeclType {
        return .structType
    }
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return [inheritanceClause?.tokens.exprTokenList,
                genericParameterClause?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter{ $0 != name }
    }

    var boundTypes: [String] {
        return inheritedTypes
    }

    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return [boundTypesAL,
                members.members.boundTypesAL,
            ].compactMap{$0}.flatMap{$0}
    }
}

extension AssociatedtypeDeclSyntax: DeclProtocol {
    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }
    var type: String {
        return name
    }

    var name: String {
        return identifier.text.trimmed.raw
    }

    var fullName: String {
        return name + "_" + boundTypes.joined()
    }

    var declType: DeclType {
        return .patType
    }

    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return [inheritanceClause?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter{$0 != name}
    }

    var boundTypes: [String] {
        return [inheritedTypes,
                initializer?.value.tokens.exprTokenList.filter{$0 != name}
            ].compactMap{$0}.flatMap{$0}
    }

    var boundTypesAL: [String] {
        return boundTypes
    }
    var refTypes: [String] {
        return boundTypesAL
    }
}

extension TypealiasDeclSyntax: DeclProtocol {
    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }
    var type: String {
        return name
    }

    var name: String {
        return identifier.text.trimmed.raw
    }

    var fullName: String {
        return name + "_" + boundTypes.joined()
    }

    var declType: DeclType {
        return .typealiasType
    }
    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return [genericParameterClause?.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter{$0 != name}
    }

    var boundTypes: [String] {
        return [inheritedTypes,
                initializer?.value.tokens.exprTokenList.filter{$0 != name}
            ].compactMap{$0}.flatMap{$0}
    }

    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return boundTypesAL
    }

}

extension PatternBindingSyntax {
    var type: String {
        if let t = typeAnnotation?.type.description.trimmed {
            return t
        }
        if let val = initializer?.value {
            if let expr = val.as(FunctionCallExprSyntax.self) {
                return expr.calledExpression.description.trimmed
            } else if let expr = val.as(ExprSyntax.self) {
                return expr.description.trimmed
            }
        }
        return .unknownVal
    }

    func boundTypes(isTransparent: Bool) -> [String] {
        var list = [String]()
        if let bound = typeAnnotation?.type.tokens.exprTokenList {
            list.append(contentsOf: bound)
        }
        if let val = initializer?.value {
            if let expr = val.as(FunctionCallExprSyntax.self) {
                let exprList = [
                    expr.calledExpression.tokens.exprTokenList,
                    expr.argumentList.map{$0.expression.tokens.exprTokenList}.flatMap{$0}
                    ].flatMap{$0}
                list.append(contentsOf: exprList)
            } else if let expr = val.as(ExprSyntax.self) {
                list.append(contentsOf: expr.tokens.exprTokenList)
            }
        }

        if isTransparent {
            if let bodyTokens = accessor?.tokens.exprTokenList {
                list.append(contentsOf: bodyTokens)
            }
        }
        return list
    }
}

extension VariableDeclSyntax: DeclProtocol {
    func declMetadatas(path: String, module: String, encloser: String, description: String, imports: [String]) -> [DeclMetadata] {
        var list = [DeclMetadata]()

        for binding in bindings {
            if let idpattern = binding.pattern.as(IdentifierPatternSyntax.self) {
                let id = idpattern.identifier.text
                if id == "_" { continue }
                let ty = binding.type
                let full = id + "_" + ty
                let bound = binding.boundTypes(isTransparent: isTransparent)
                let val = DeclMetadata(path: path,
                                       module: module,
                                       imports: imports,
                                       encloser: encloser,
                                       name: id,
                                       type: ty,
                                       fullName: full,
                                       description: description,
                                       declType: declType,
                                       inheritedTypes: inheritedTypes,
                                       boundTypes: bound,
                                       boundTypesAL: bound,
                                       isPublicOrOpen: accessLevel.isPublicOrOpen,
                                       isOverride: isOverride,
                                       used: false)
                list.append(val)
            } else if let tuple = binding.pattern.as(TuplePatternSyntax.self) {
                for el in tuple.elements {
                    if let idpattern = el.pattern.as(IdentifierPatternSyntax.self) {
                        let id = idpattern.identifier.text
                        if id == "_" { continue }

                        let ty = binding.type
                        let full = id + "_" + ty
                        let bound = binding.boundTypes(isTransparent: isTransparent)
                        let val = DeclMetadata(path: path,
                                               module: module,
                                               imports: imports,
                                               encloser: encloser,
                                               name: id,
                                               type: ty,
                                               fullName: full,
                                               description: description,
                                               declType: declType,
                                               inheritedTypes: inheritedTypes,
                                               boundTypes: bound,
                                               boundTypesAL: bound,
                                               isPublicOrOpen: accessLevel.isPublicOrOpen,
                                               isOverride: isOverride,
                                               used: false)
                        list.append(val)
                    }
                }
            }
        }

        return list
    }

    var isTransparent: Bool {
        return attributesDescription.contains(String.transparent)
    }

    var name: String {
        let ret = bindings.compactMap { $0.pattern.description.trimmed }.joined().raw
        return ret
    }

    var inheritedTypes: [String] {
        return []
    }

    var isExprOrStmt: Bool {
        return false
    }

    var fullName: String {
        return name + "_" + type
    }

    var declType: DeclType {
        return .varType
    }

    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var isOverride: Bool {
        return modifiers?.isOverride ?? false
    }

    var attributesDescription: String {
        return attributes?.trimmedDescription ?? ""
    }

    var type: String {
        return bindings.first?.type ?? ""
    }

    var boundTypes: [String] {
        var list = [String]()
        for b in bindings {
            list.append(contentsOf: b.boundTypes(isTransparent: isTransparent))
        }
        if let attrs = attributes?.tokens.exprTokenList {
            list.append(contentsOf: attrs)
        }
        return list.filter{$0 != name}
    }

    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        let ret = [boundTypesAL,
                bindings.compactMap{$0.accessor?.tokens.exprTokenList}.flatMap{$0}
            ].compactMap{$0}.flatMap{$0}
        return ret
    }
}


extension FunctionDeclSyntax: DeclProtocol {
    var name: String {
        return self.identifier.description.trimmed.raw
    }

    var type: String {
        return signature.output?.returnType.description.trimmed ?? ""
    }

    var fullName: String {
        return name + signature.description.trimmed
    }

    var declType: DeclType {
        if self.identifier.tokenKind == .spacedBinaryOperator(self.identifier.text) {
            return .operatorType
        }
        return .funcType
    }

    var accessLevel: String {
        return self.modifiers?.acl ?? ""
    }

    var isOverride: Bool {
        return modifiers?.isOverride ?? false
    }

    var attributesDescription: String {
        return attributes?.trimmedDescription ?? ""
    }

    var inheritedTypes: [String] {
        return []
    }

    var isExprOrStmt: Bool {
        return false
    }

    var boundTypes: [String] {
        let genericParamTypes = genericParameterClause?.genericParameterList.tokens.exprTokenList
        let genericWhereTypes = genericWhereClause?.tokens.exprTokenList
        let paramTypes = signature.input.parameterList.compactMap{$0.type?.tokens.exprTokenList}.flatMap{$0}
        let paramVals = signature.input.parameterList.compactMap{$0.defaultArgument?.value.tokens.exprTokenList}.flatMap{$0}
        let returnTypes = signature.output?.returnType.tokens.exprTokenList
        let attrs = attributes?.tokens.exprTokenList  // e.g. @FunctionBuilder
        var list = [genericParamTypes, genericWhereTypes, paramTypes, paramVals, returnTypes, attrs].compactMap{$0}.flatMap{$0}
        if attributesDescription.contains(String.transparent) {
            if let bodyTokens = body?.tokens.exprTokenList {
                list.append(contentsOf: bodyTokens)
            }
        }
        return list.filter{$0 != name}
    }
    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return [boundTypesAL,
                body?.tokens.exprTokenList
            ].compactMap{$0}.flatMap{$0}
    }
}

extension InitializerDeclSyntax: DeclProtocol {
    var name: String {
        return "init"
    }
    var type: String {
        return name
    }

    var fullName: String {
        return name + "_" + parameters.description.trimmed
    }

    var declType: DeclType {
        return .initType
    }

    var isOverride: Bool {
        return modifiers?.isOverride ?? false
    }

    var attributesDescription: String {
        return attributes?.trimmedDescription ?? ""
    }

    var accessLevel: String {
        return modifiers?.acl ?? ""
    }

    var boundTypes: [String] {
        let genericParamTypes = genericParameterClause?.genericParameterList.tokens.exprTokenList
        let genericWhereTypes = genericWhereClause?.tokens.exprTokenList

        var paramList = [String]()
        for param in parameters.parameterList {
            if let pval = param.defaultArgument?.value {
                if let accessed = pval.as(MemberAccessExprSyntax.self), let base = accessed.base {
                    paramList.append(accessed.description.trimmed)
                    paramList.append(contentsOf: base.tokens.exprTokenList)
                } else {
                    paramList.append(contentsOf: pval.tokens.exprTokenList)
                }
            }
            if let ptypes = param.type?.tokens.exprTokenList {
                paramList.append(contentsOf: ptypes)
            }
        }

        var list = [genericParamTypes, genericWhereTypes, paramList].compactMap{$0}.flatMap{$0}

        // @_transparent on public or @usableFromInline functions require all types in sig and body to be public
        if attributesDescription.contains(String.transparent) {
            if let bodyTokens = body?.tokens.exprTokenList {
                list.append(contentsOf: bodyTokens)
            }
        }

        return list.filter{$0 != name}
    }
    var boundTypesAL: [String] {
        return boundTypes
    }

    var refTypes: [String] {
        return [boundTypesAL,
                body?.tokens.exprTokenList
            ].compactMap{$0}.flatMap{$0}
    }

    var inheritedTypes: [String] {
        return []
    }

    var isExprOrStmt: Bool {
        return false
    }

    func isRequired(with declType: DeclType) -> Bool {
        if declType == .protocolType {
            return true
        } else if declType == .classType {
            if let modifiers = self.modifiers {
                
                if modifiers.isConvenience {
                    return false
                }
                return modifiers.isRequired
            }
        }
        return false
    }
}

extension SubscriptDeclSyntax: DeclProtocol {

    var fullName: String {
        return name + "_" + result.returnType.description.trimmed
    }

    var name: String {
        return self.subscriptKeyword.text
    }

    var declType: DeclType {
        return .subscriptType
    }

    var accessLevel: String {
        return modifiers?.acl ?? ""
    }

    var inheritedTypes: [String] {
        return []
    }

    var isExprOrStmt: Bool {
        return false
    }
    var isOverride: Bool {
        return false
    }

    var type: String {
        return result.returnType.description.trimmed
    }

    var boundTypes: [String] {
        return [result.returnType.tokens.exprTokenList,
                genericParameterClause?.genericParameterList.tokens.exprTokenList,
                genericWhereClause?.tokens.exprTokenList,
                attributes?.tokens.exprTokenList,
            ].compactMap{$0}.flatMap{$0}.filter {$0 != name }
    }

    var boundTypesAL: [String] {
        return boundTypes
    }


    var refTypes: [String] {
        return [boundTypesAL,
                accessor?.tokens.exprTokenList
            ].compactMap{$0}.flatMap{$0}
    }

}


// MARK -

extension AttributeListSyntax {
    var trimmedDescription: String? {
        return self.withoutTrivia().description.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

extension ModifierListSyntax {
    var acl: String {
        for modifier in self {
            for token in modifier.tokens {
                switch token.tokenKind {
                case .publicKeyword, .internalKeyword, .privateKeyword, .fileprivateKeyword:
                    return token.text
                default:
                    // For some reason openKeyword option is not available in TokenKind so need to address separately
                    if token.text == String.open {
                        return token.text
                    }
                }
            }
        }
        return ""
    }

    var isStatic: Bool {
        return self.tokens.filter {$0.tokenKind == .staticKeyword }.count > 0
    }

    var isRequired: Bool {
        return self.tokens.filter {$0.text == String.required }.count > 0
    }

    var isConvenience: Bool {
        return self.tokens.filter {$0.text == String.convenience }.count > 0
    }

    var isOverride: Bool {
        return self.tokens.filter {$0.text == String.override }.count > 0
    }

    var isFinal: Bool {
        return self.tokens.filter {$0.text == String.final }.count > 0
    }

    var isPrivate: Bool {
        return self.tokens.filter {$0.tokenKind == .privateKeyword || $0.tokenKind == .fileprivateKeyword }.count > 0
    }

    var isPublic: Bool {
        return self.tokens.filter {$0.tokenKind == .publicKeyword }.count > 0
    }

    var isOpen: Bool {
        return self.tokens.filter {$0.text == String.open }.count > 0
    }
}

extension Trivia {
    // This parses arguments in annotation which can be used to override certain types.
    //
    // E.g. given /// @mockable(typealias: T = Any; U = AnyObject), it returns
    // a dictionary: [T: Any, U: AnyObject] which will be used to override inhertied types
    // of typealias decls for T and U.
    private func metadata(with annotation: String, in val: String) -> AnnotationMetadata? {
        if val.contains(annotation) {
            let comps = val.components(separatedBy: annotation)
            var ret = AnnotationMetadata()
            if var argsStr = comps.last, !argsStr.isEmpty {
                if argsStr.hasPrefix("(") {
                    argsStr.removeFirst()
                }
                if argsStr.hasSuffix(")") {
                    argsStr.removeLast()
                }
                if argsStr.contains(String.typealiasColon), let subStr = argsStr.components(separatedBy: String.typealiasColon).last, !subStr.isEmpty {
                    ret.typeAliases = subStr.arguments(with: .annotationArgDelimiter)
                }
                if argsStr.contains(String.moduleColon), let subStr = argsStr.components(separatedBy: String.moduleColon).last, !subStr.isEmpty {
                    let val = subStr.arguments(with: .annotationArgDelimiter)
                    ret.module = val?[.prefix]
                }
                if argsStr.contains(String.rxColon), let subStr = argsStr.components(separatedBy: String.rxColon).last, !subStr.isEmpty {
                    ret.varTypes = subStr.arguments(with: .annotationArgDelimiter)
                }
                if argsStr.contains(String.varColon), let subStr = argsStr.components(separatedBy: String.varColon).last, !subStr.isEmpty {
                    if let val = subStr.arguments(with: .annotationArgDelimiter) {
                        if ret.varTypes == nil {
                            ret.varTypes = val
                        } else {
                            ret.varTypes?.merge(val, uniquingKeysWith: {$1})
                        }
                    }
                }
            }
            return ret
        }
        return nil
    }
    
    // Looks up an annotation (e.g. /// @mockable) and its arguments if any.
    // See metadata(with:, in:) for more info on the annotation arguments.
    func annotationMetadata(with annotation: String) -> AnnotationMetadata? {
        guard !annotation.isEmpty else { return nil }
        var ret: AnnotationMetadata?
        for i in 0..<count {
            let trivia = self[i]
            switch trivia {
            case .docLineComment(let val):
                ret = metadata(with: annotation, in: val)
                if ret != nil {
                    return ret
                }
            case .docBlockComment(let val):
                ret = metadata(with: annotation, in: val)
                if ret != nil {
                    return ret
                }
            default:
                continue
            }
        }
        return nil
    }
}


extension TokenSyntax {
    var stringToken: String? {
        if text == "self" || text == "Self" || text == "super" || text == "nil" ||
            text == "as" || text == "true" || text == "false" || text == "AnyObject" || text == "Any" {
            return nil
        }

        let startsWithLetter = text.first?.isLetter ?? false
        let validFirstChar = text.first == "_" || startsWithLetter
        if (validFirstChar && (text.isAlphanumeric || text.contains("_"))) ||
            tokenKind == .spacedBinaryOperator(text) {
            return text
        }

        return nil
    }

    var exprToken: String? {
        if tokenKind != .stringQuote,
            tokenKind != .stringSegment(text),
            tokenKind != .spacedBinaryOperator(text),
            tokenKind != .initKeyword {

            return stringToken
        }
        return nil
    }

    func filteredToken(with suffix: String) -> String? {
        if text.hasSuffix(suffix) {
            return String(text.dropLast(suffix.count))
        }
        return nil
    }
}

extension TokenSequence {
    var tokenList: [String] {
        return self.compactMap { $0.stringToken }
    }

    var exprTokenList: [String] {
        return self.compactMap { $0.exprToken }
    }
}




================================================
FILE: Sources/SwiftCodeSanKit/Utils/Extensions/SyntaxParserExtensions.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//


import Foundation
import SwiftSyntax
import SwiftSyntaxParser

extension SyntaxParser {
    public static func parse(_ fileData: Data, path: String,
                             diagnosticHandler: ((Diagnostic) -> Void)? = nil) throws -> SourceFileSyntax {
        // Avoid using `String(contentsOf:)` because it creates a wrapped NSString.
        let source = fileData.withUnsafeBytes { buf in
            return String(decoding: buf.bindMemory(to: UInt8.self), as: UTF8.self)
        }
        return try parse(source: source, filenameForDiagnostics: path,
                         diagnosticHandler: diagnosticHandler)
    }

    public static func parse(_ path: String) throws -> SourceFileSyntax {
        guard let fileData = FileManager.default.contents(atPath: path) else {
            fatalError("Retrieving contents of \(path) failed")
        }
        return try parse(fileData, path: path)
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Utils/Logger.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation
import os.signpost

fileprivate let perfLog = OSLog(subsystem: "SwiftCodeSan", category: "PointsOfInterest")
fileprivate var prevTime: CFAbsoluteTime?
fileprivate var startTime: CFAbsoluteTime?

public var minLogLevel = 0

/// Logs status and other messages depending on the level provided
public enum LogLevel: Int {
    case verbose
    case info
    case warning
    case error
}

public func logTotalElapsed(_ arg: Any...) {
    let cur = CFAbsoluteTimeGetCurrent()
    if let startTime = startTime {
        print(arg, cur-startTime)
    } else {
        print("0.00")
    }
}

public func logTime(_ arg: Any...) {
    let cur = CFAbsoluteTimeGetCurrent()
    if let prevTime = prevTime {
        var str = arg
        let delta = (cur-prevTime)
        str.append("Took \(delta)")
        print(str)
    }
    prevTime = cur
    if startTime == nil {
        startTime = cur
    }
}

public func log(_ arg: Int, level: LogLevel = .info, interval: Int) {
    if arg > 0, arg % interval == 0 {
        log(arg, level: level)
    }
}

public func log(_ arg: Any..., level: LogLevel = .info, counter: inout Int, interval: Int, timed: Bool = false) {
    if counter > 0, counter % interval == 0 {
        log(arg, counter, level: level)
        if timed {
            logTime()
        }
    }
    counter += 1
}

public func log(_ arg: Any..., level: LogLevel = .info) {
    guard level.rawValue >= minLogLevel else { return }
    switch level {
    case .info, .verbose:
        print(arg)
    case .warning:
        print("WARNING: \(arg)")
    case .error:
        print("ERROR: \(arg)")
    }
}

public func signpost_begin(name: StaticString) {
    if minLogLevel == LogLevel.verbose.rawValue {
        os_signpost(.begin, log: perfLog, name: name)
    }
}

public func signpost_end(name: StaticString) {
    if minLogLevel == LogLevel.verbose.rawValue {
        os_signpost(.end, log: perfLog, name: name)
    }
}


================================================
FILE: Sources/SwiftCodeSanKit/Utils/Scanner.swift
================================================
//
//  Copyright (c) 2018. Uber Technologies
//
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//
//  http://www.apache.org/licenses/LICENSE-2.0
//
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
//

import Foundation

public var scanConcurrencyLimit: Int? = nil

func semaphore(_ numThreads: Int?) -> DispatchSemaphore? {
    let limit = concurrencyLimit(numThreads)
    var sema: DispatchSemaphore?
    if limit > 1 {
        sema = DispatchSemaphore(value: limit)
    }
    return sema
}

func queue(_ numThreads: Int?) -> DispatchQueue? {
    var q: DispatchQueue?
    if concurrencyLimit(numThreads) > 1 {
        q = DispatchQueue(label: "SwiftCodeSan-queue", qos: DispatchQoS.userInteractive, attributes: DispatchQueue.Attributes.concurrent)
    }
    return q
}

func concurrencyLimit(_ numThreads: Int?) -> Int {
    var limit = 1
    if let n = numThreads {
        limit = n
    } else if let n = scanConcurrencyLimit {
        limit = n
    }
    return limit
}

public func scan(_ paths: [String],
                 isDirectory: Bool,
                 numThreads: Int? = nil,
                 block: @escaping (_ path: String, _ lock: NSLock?) -> ()) {
    if isDirectory {
        scan(dirs: paths, block: block)
    } else {
        scan(paths, block: block)
    }
}

public func scan(dirs: [String],
                 numThreads: Int? = nil,
                 block: @escaping (_ path: String, _ lock: NSLock?) -> ()) {
    
    if let queue = queue(numThreads) {
        let sema = semaphore(numThreads)
        let lock = NSLock()
        scanDirs(dirs) { filePath in
            _ = sema?.wait(timeout: DispatchTime.distantFuture)
            queue.async {
                block(filePath, lock)
                sema?.signal()
            }
        }
        // Wait for queue to drain
        queue.sync(flags: .barrier) {}
    } else {
        scanDirs(dirs) { filePath in
            block(filePath, nil)
        }
    }
}

public func scan<T>(_ elements: [T],
                    numThreads: Int? = nil,
                    block: @escaping (T, NSLock?) -> ()) {
    
    if let queue = queue(numThreads) {
        let sema = semaphore(numThreads)
        let lock = NSLock()
        for element in elements {
            _ = sema?.wait(timeout: DispatchTime.distantFuture)
            queue.async {
                block(element, lock)
                sema?.signal()
            }
        }
        queue.sync(flags: .barrier) { }
    } else {
        for element in elements {
            block(element, nil)
        }
    }
}

public func scan<T, U>(_ elements: [T: U],
                       numThreads: Int? = nil,
                       block: @escaping (T, U, NSLock?) -> ()) {
    
    if let queue = queue(numThreads) {
        let sema = semaphore(numThreads)
        let lock = NSLock()
        
        for element in elements {
            _ = sema?.wait(timeout: DispatchTime.distantFuture)
            queue.async {
                block(element.key, element.value, lock)
                sema?.signal()
            }
        }
        queue.sync(flags: .barrier) { }
    } else {
        for element in elements {
            block(element.key, element.value, nil)
        }
    }
}


public func scanDirs(_ paths: [String], with callBack: (String) -> Void) {
    for path in paths {
        scanDir(path, with: callBack)
    }
}

func scanDir(_ path: String, with callBack: (String) -> Void) {
    let errorHandler = { (url: URL, error: Error) -> Bool in
        fatalError("Failed to traverse \(url) with error \(error).")
    }
    if let enumerator = FileManager.default.enumerator(at: URL(fileURLWithPath: path, isDirectory: true), includingPropertiesForKeys: nil, options: [.skipsHiddenFiles], errorHandler: errorHandler) {
        while let nextObjc = enumerator.nextObject() {
            if let fileUrl = nextObjc as? URL {
                callBack(fileUrl.path)
            }
        }
    }
}


================================================
FILE: Tests/SwiftCodeSanTestCase.swift
================================================
import XCTest
import SwiftCodeSanKit

class SwiftCodeSanTestCase: XCTestCase {
    var srcFilePathsCount = 1
    var mockFilePathsCount = 1
    
    let bundle = Bundle(for: SwiftCodeSanTestCase.self)
    
    lazy var dstFilePath: String = {
        return bundle.bundlePath + "/Dst.swift"
    }()
    
    lazy var srcFilePaths: [String] = {
        var idx = 0
        var paths = [String]()
        let prefix = bundle.bundlePath + "/Src"
        let suffix = ".swift"
        while idx < srcFilePathsCount {
            let path = prefix + "\(idx)" + suffix
            paths.append(path)
            idx += 1
        }
        return paths
    }()
    
    lazy var mockFilePaths: [String] = {
        var idx = 0
        var paths = [String]()
        let prefix = bundle.bundlePath + "/Mocks"
        let suffix = ".swift"
        while idx < mockFilePathsCount {
            let path = prefix + "\(idx)" + suffix
            paths.append(path)
            idx += 1
        }
        return paths
    }()
    
    override func setUp() {
        // Put setup code here. This method is called before the invocation of each test method in the class.
        let created = FileManager.default.createFile(atPath: dstFilePath, contents: nil, attributes: nil)
        XCTAssert(created)
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        try? FileManager.default.removeItem(atPath: dstFilePath)
        for srcpath in srcFilePaths {
            try? FileManager.default.removeItem(atPath: srcpath)
        }
    }
    
    func verify(srcContent: String, dstContent: String, header: String = "", declType: DeclType = .protocolType, useTemplateFunc: Bool = false, testableImports: [String]? = [], concurrencyLimit: Int? = 1) {
        verify(srcContents: [srcContent], dstContent: dstContent, header: header, declType: declType, useTemplateFunc: useTemplateFunc, testableImports: testableImports, concurrencyLimit: concurrencyLimit)
    }
    
    func verify(srcContents: [String], dstContent: String, header: String, declType: DeclType, useTemplateFunc: Bool, testableImports: [String]?, concurrencyLimit: Int?) {
        var index = 0
        srcFilePathsCount = srcContents.count
        
        for src in srcContents {
            if index < srcContents.count {
                let srcCreated = FileManager.default.createFile(atPath: srcFilePaths[index], contents: src.data(using: .utf8), attributes: nil)
                index += 1
                XCTAssert(srcCreated)
            }
        }
        // TODO: Pass in srcfile path - module list
        removeDeadDecls(filesToModules: ["test1.swift": "test1"],
                        whitelist: nil,
                        topDeclsOnly: true,
                        inplace: true,
                        testFiles: [],
                        inplaceTests: false,
                        logFilePath: nil,
                        concurrencyLimit: nil,
                        onCompletion: {
                            let output = (try? String(contentsOf: URL(fileURLWithPath: self.dstFilePath), encoding: .utf8)) ?? ""
                            let outputContents = output.components(separatedBy:  .whitespacesAndNewlines).filter{!$0.isEmpty}
                            XCTAssert(outputContents.count > 0)
                        })
    }
}



================================================
FILE: Tests/TestClasses/Fixtures/test0.swift
================================================
#if TESTFILE
extension Integration {
    var isUnitTest: Bool {
        #if TEST
        if case .test = RunType.current {
            return !(Handler.isHandlerActive() || runner.sharedInstance.isActive)
        }
        #else
        let x = SomeType().someMethod()
        return x
        #endif
    }

    var workersProviderMock: IntegrationProviderMock { shared { IntegrationProviderMock() } }
}


let s = SwipeTransitionController()

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test1.swift
================================================
#if TESTFILE
import Foundation

public protocol FileHandler {

    func createFile(atPath: String, contents: Data?, attributes: [FileAttributeKey: Any]?) -> Bool
    func moveItem(at url: URL, to: URL) throws

    func contentsOfDirectory(at url: URL, includingPropertiesForKeys keys: [URLResourceKey]?, options mask: FileManager.DirectoryEnumerationOptions) throws -> [URL]
    func createDirectory(at url: URL, withIntermediateDirectories createIntermediates: Bool, attributes: [FileAttributeKey: Any]?) throws
    func fileExists(atPath: String) -> Bool

    func createFileHandle(forWritingToURL: URL) throws -> FileHandle

    func read(contentsOf url: URL) throws -> String
    @discardableResult
    func write(dictionary: [String: Any], to url: URL, atomically: Bool) -> Bool
    func write(data: Data, to url: URL, options: Data.WritingOptions) throws

    func read(dictionaryAt url: URL) -> [String: Any]?

    func data(forURL url: URL) -> Data?
    func isDeletableFile(atURL url: URL) -> Bool

    func removeItem(at URL: URL) throws

    func url(for directory: FileManager.SearchPathDirectory, in domain: FileManager.SearchPathDomainMask) throws -> URL

    func urls(for directory: FileManager.SearchPathDirectory, in domainMask: FileManager.SearchPathDomainMask) -> [URL]
    func copyItem(at srcURL: URL, to dstURL: URL) throws

    func setResourceValues(_ values: URLResourceValues, at url: inout URL) throws

}

extension FileManager: FileHandler {
    public func write(data: Data, to url: URL, options: Data.WritingOptions) throws {
        try data.write(to: url, options: options)
    }

    public func createFileHandle(forWritingToURL: URL) throws -> FileHandle {
        return try FileHandle(forWritingTo: forWritingToURL)
    }

    public func read(contentsOf url: URL) throws -> String {
        return try String(contentsOf: url, encoding: String.Encoding.utf8)
    }

    public func write(dictionary: [String: Any], to url: URL, atomically: Bool) -> Bool {
        return (dictionary as NSDictionary).write(to: url as URL, atomically: atomically)
    }

    public func read(dictionaryAt url: URL) -> [String: Any]? {
        return NSDictionary(contentsOf: url as URL) as? [String: Any]
    }

    public func data(forURL url: URL) -> Data? {
        return try? Data(contentsOf: url)
    }

    public func isDeletableFile(atURL url: URL) -> Bool {
        return isDeletableFile(atPath: url.path)
    }

    public func url(for directory: FileManager.SearchPathDirectory, in domain: FileManager.SearchPathDomainMask) throws -> URL {
        return try url(for: directory, in: domain, appropriateFor: nil, create: true)
    }

    public func setResourceValues(_ values: URLResourceValues, at url: inout URL) throws {
        try url.setResourceValues(values)
    }
}
#endif


================================================
FILE: Tests/TestClasses/Fixtures/test2.swift
================================================
//
//  Copyright © Uber Technologies, Inc. All rights reserved.
//
#if TESTFILE

import Foundation

@_transparent public func testAssert(_ resumable: Bool) {
    let x = AssertStaticString()
    print(x)
    
    if testAssertionDisabled {
        return
    }
    reassert(message(), file: unsafeBitCast(assertStaticString, to: StaticString.self), function: function, line: line)
    
    
    let handler = AssertionHandlers.assertionFailure
    print(handler)
}

class AssertionHandlers {
}


public struct AssertStaticString {
    public init() {}
    public func check() -> Int {
        return 5
    }
}

public extension String {
    func someAssert(_ assertStaticString: AssertStaticString) {
    }
}


public typealias AssertionHandler = (String?, data: LogModelMetadata?, line: UInt)


public let testAssertionDisabled: Bool = {
    return false
}()

public func reassert(_ message: String, file: StaticString, function: String, line: UInt) {
}


public class SynchronizedFoo {
    
}

public protocol KeyValueSubscripting {
    associatedtype Key
    associatedtype Value
    subscript(key: Key) -> Value? { get set }
}

public extension SynchronizedFoo: KeyValueSubscripting where T: KeyValueSubscripting {
    public subscript(key: T.Key) -> T.Value? {
        get {
            return read { (collection) -> T.Value? in
                return collection[key]
            }
        }
        set {
            write { (collection) -> () in
                collection[key] = newValue
            }
        }
    }

    public func bar() -> String {
        return "bar"
    }

}
#endif


================================================
FILE: Tests/TestClasses/Fixtures/test3.swift
================================================
#if TESTFILE


import Foundation

public protocol ProtocolY {
    func filterBy(uuid: String) -> Int?
}
public protocol ProtocolX: ProtocolY {
}


public final class FooKlass {
    public static var sharedInstance = FooKlass()

    func bar(arg: String) -> Int {
        let (lhs, rhs) = arg

        let ret = Double()
        ret.listener = lhs
        return ret
    }
}

final class ListInteractor {
    public var x: String = "", y: Int, z: Double

    public var (v, w): (String, Int) = ("", 0)

    public let
   application: UIApplicationProtocol,
   cachedExperiments: CachedExperimenting,
   deepLinkBuilder: (_ url: URL) -> DetailsDeeplink?,
   detailsBuilder: DetailsBuildable,
   listStream: Observable<[DetailsListStreaming]>
}

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test4.swift
================================================
#if TESTFILE


import Foundation

public enum BarType {
    case current
}


class Klass: P {
    private static var unusedP: SomeType = SomeType()


    public init() {
    }

    func isActive(_ arg: AmbProtocol?) -> Bool {
        arg?.usedFunc()
        return true
    }

    public override func isApplicable(context: SomeContext, with flag: Flag) -> Bool {
        return flag.isActive
    }

    func unusedFunc() {
        print("...")
    }
}


public final class OtherKlass {
    public static var sharedInstance = OtherKlass()

    public var shouldRun: Bool {
        let (lhs, mhs, rhs) = asdf()
        print(lhs, rhs)

        return false
    }
}

public protocol Flag {
}

public extension Flag {
    var isActive: Bool {
        return true
    }

    var isActiveLong: Bool {
        return isEnabled(for: "Exp1") || isEnabled(for: "Exp2")
    }
}

public protocol YProtocol {
    var unusedY: String { get }
}

public extension YProtocol {
    func usedZ() {
    }
}

protocol UnusedInternal {
}

extension UnusedInternal {
    func unusedX() {
    }
}

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test5.swift
================================================
#if TESTFILE

enum State {
    case eat(SomeFood)
    case sleep(SomeTime)
}

public func toJSON(arg: String) -> JSON {
    var dict = [String: JSON]()

    switch arg {
        case .eat(let value):
            print(value)
        case .sleep(let value):
            dict["state"] = value
    }

    return .dictionary(dict)
}



public typealias T = (String, EventHandler)
public typealias EventHandler<StreamingResponse> = (Bool, StreamingResponse?, Error?) -> ()

#if SOME_CONDITION

    public extension String {
        func withAssertion(_ block: (_ str: StaticString) -> ()) {
            if let stringData = self.data(using: .utf8, allowLossyConversion: false) {
                var x = StaticString()
                x._utf8CodeUnitCount = stringData.count
                stringData.withUnsafeBytes { rawBufferPointer in
                    if let rawPtr = rawBufferPointer.baseAddress {
                        x._startPtrOrData = Int(bitPattern: rawPtr)
                    }
                    block(x)
                }
            }
        }
    }

#endif

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test6.swift
================================================
#if TESTFILE

import Foundation
import Test1


private let error = NSError(domain: TestDomain, code: InvalidData, userInfo: nil)

extension String {

    static func instance(from data: Data) throws -> String {
        let result = self.init(data: data, encoding: .utf8)
        if let result = result {
            return result
        } else {
            throw error
        }
    }
}

let SomeSingleton = SomeSingletonObject()

// This is a comment
// doc comment
// asdf
public  protocol Bar: P1, P2 {
}

public typealias P1 = Cat.P1

public protocol XAB {
    subscript(key: Int) -> String? { get }
}

class KEY {}

extension KEY: XAB {
    public subscript(key: Int) -> String? {
        return nil
    }

    func bar() {

    }

    var debugDescription: String {
        return "this is KEY"
    }
}



public protocol ObjectReference {

}

public protocol P {
    func iteratedObjects(_ object: Any) -> [ObjectReference]
}

public final class SynchronousKeyValueStoreWrapper<Key>: SynchronousKeyValueAssociating where Key: StoredKeying {
    var base: KeyValueStore<Key>?


    public subscript(key: T.Key) -> T.Value? {
        return nil
    }

}

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test7.swift
================================================
#if TESTFILE

extension Foo {
    #if DEBUG
        var baz: Bool {
            if case .test = RunType.current {
                return !(Handler.isHandlerActive() || runner.sharedInstance.isActive)
            }
            return false
        }

        var zmock: ZMock { shared { ZMock() } }
        fileprivate var zvar: Z { shared { Z(with: self) } }
    #else
        fileprivate var cat: Z { shared { Z(with: self) } }
    #endif
}

import Test6

let k = Klass()

class Klass: P {
    public func iteratedObjects(_ object: Any) -> [Result] {
        return []
    }

    private static var loadedFonts: Synchronized<[String: UIFont]> = Synchronized()

    public init() {
        testAssert(true)
    }

    public init(store: KeyValueStore<Key>,
                storeKey: Key,
                target: Int,
                initialHitTargetValue: Bool? = nil) {
        self.store = store
        self.storeKey = storeKey
        self.target = target + 1
        self.hitTargetSubject = ReplaySubject<Bool>.create(bufferSize: 1)

        queue.async {
            let count = store.synchronously(operate: { (container: SynchronousKeyValueStoreContainer<Key>?) -> Int? in
                return container?.item(for: storeKey)
            })
            print(count)
        }
    }
}

public final class KeyValueStore<Key> where Key: StoredKeying {
    public func synchronously<T>(operations: (SynchronousKeyValueStoreContainer<Key>?) -> T) -> T {
        let container = SynchronousKeyValueStoreContainer(self)
        let result = operations(container)
        container.invalidate()
        return result
    }
}

#endif


================================================
FILE: Tests/TestClasses/Fixtures/test8.swift
================================================
#if TESTFILE

import Test9
import Test10
import Foundation

class Foo: ProtocolX {
    func filterBy(uuid: String) -> Int? {
        return nil
    }

    var bar: Bool {
        let found = FooKlass.sharedInstance.bar(arg)
        let notFound = ListInteractor.application

        if found, !notFound {
            return true
        }
    }

    var baz: String {
        if case let .someCase = BarType.current {
            return !(Klass().isActive() ||
                Klass().isApplicable() ||
                OtherKlass.sharedInstance.shouldRun)
        }
        return ""
    }
}

let foo = Foo()

#endif


================================================
FILE: Tests/TestClasses/SwiftCodeSanTests.swift
================================================
import Foundation

class DCETests: SwiftCodeSanTestCase {
    
    func testExample() {
        // TODO: add a test
    }
    
}



================================================
FILE: install-script.sh
================================================
#!/bin/bash

# Causes the shell to exit if any subcommand returns a non-zero status
set -e

showhelp() {
    echo "Description: Builds and installs target specified
Usage: -s/--source-dir [source dir], -t/--target [name of target to build/install], -d/--destination-dir [destination dir], -o/--output [output file name in tar.gz]"
    exit
}

if [[ $1 == "" ]] 
then
showhelp
fi

realpath() {
    [[ $1 = /* ]] && echo "$1" || echo "$PWD"
}

while [[ $# -gt 0 ]]
do
key="$1"

case $key in
    -s|--source-dir)
    SRCDIR=$(realpath "$2")
    shift # past argument
    shift # past value
    ;;
    -d|--destination-dir)
    DESTDIR=$(realpath "$2")
    shift # past argument
    shift # past value
    ;;
    -t|--target)
    TARGET="$2"
    shift # past argument
    shift # past value
    ;;
    -o|--output)
    OUTFILE="$2"
    shift # past argument
    shift # past value
    ;;
    -h|--help)
    showhelp
    ;;
    *) 
    showhelp
    ;;
esac
done


CUR=$PWD

echo "** Clean/Build..."

echo "SOURCE DIR = ${SRCDIR}"
echo "TARGET = ${TARGET}"
echo "DESTINATION DIR = ${DESTDIR}"
echo "OUTPUT FILE = ${OUTFILE}"

cd "$SRCDIR"
rm -rf .build
swift build -c release 

cd .build/release

echo "** Install..."
cp "$(xcode-select -p)"/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx/lib_InternalSwiftSyntaxParser.dylib . 

install_name_tool -change @rpath/lib_InternalSwiftSyntaxParser.dylib @executable_path/lib_InternalSwiftSyntaxParser.dylib "$TARGET"

tar -cvzf "$OUTFILE" "$TARGET" lib_InternalSwiftSyntaxParser.dylib

mv "$OUTFILE" "$DESTDIR"

cd "$CUR"

echo "** Output file is at $DESTDIR/$OUTFILE"
echo "** Done."
Download .txt
gitextract__6ffq4gh/

├── .github/
│   └── workflows/
│       └── ci.yml
├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE.txt
├── NOTICE.txt
├── Package.swift
├── README.md
├── Sources/
│   ├── SwiftCodeSan/
│   │   ├── Executor.swift
│   │   └── main.swift
│   └── SwiftCodeSanKit/
│       ├── Core/
│       │   ├── AccessLevelRewriter.swift
│       │   ├── DeclMetaTypes.swift
│       │   ├── DeclRemover.swift
│       │   ├── DeclVisitor.swift
│       │   ├── ImportRewriter.swift
│       │   └── RefChecker.swift
│       ├── FileParsers/
│       │   └── DeclParser.swift
│       ├── FileUpdaters/
│       │   └── DeclUpdater.swift
│       ├── Operations/
│       │   ├── RemoveDeadDecls.swift
│       │   ├── RemoveUnusedImports.swift
│       │   └── UpdateAccessLevels.swift
│       └── Utils/
│           ├── Extensions/
│           │   ├── FileManagerExtensions.swift
│           │   ├── SequenceExtensions.swift
│           │   ├── StringExtensions.swift
│           │   ├── SyntaxExtensions.swift
│           │   └── SyntaxParserExtensions.swift
│           ├── Logger.swift
│           └── Scanner.swift
├── Tests/
│   ├── SwiftCodeSanTestCase.swift
│   └── TestClasses/
│       ├── Fixtures/
│       │   ├── test0.swift
│       │   ├── test1.swift
│       │   ├── test2.swift
│       │   ├── test3.swift
│       │   ├── test4.swift
│       │   ├── test5.swift
│       │   ├── test6.swift
│       │   ├── test7.swift
│       │   └── test8.swift
│       └── SwiftCodeSanTests.swift
└── install-script.sh
Condensed preview — 40 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (199K chars).
[
  {
    "path": ".github/workflows/ci.yml",
    "chars": 297,
    "preview": "name: CI\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\njobs:\n  macos:\n    runs-on: macOS-lat"
  },
  {
    "path": ".gitignore",
    "chars": 534,
    "preview": "## Xcode projects\n*.xcodeproj\n*.xcworkspace\n\n## Build generated\nbuild/\nDerivedData/\n\n## Various settings\n*.pbxuser\n!defa"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "chars": 3224,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 3224,
    "preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
  },
  {
    "path": "LICENSE.txt",
    "chars": 11358,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "NOTICE.txt",
    "chars": 642,
    "preview": "SwiftMockGen depends on the following libraries:\n\nSwift Package Manager (https://github.com/apple/swift-package-manager)"
  },
  {
    "path": "Package.swift",
    "chars": 1243,
    "preview": "// swift-tools-version:5.2\nimport PackageDescription\n\nlet package = Package(\n    name: \"SwiftCodeSan\",\n    platforms: [\n"
  },
  {
    "path": "README.md",
    "chars": 6321,
    "preview": "# ![](Images/logo.png)\n[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2964/badge)](https:/"
  },
  {
    "path": "Sources/SwiftCodeSan/Executor.swift",
    "chars": 9978,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSan/main.swift",
    "chars": 802,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/AccessLevelRewriter.swift",
    "chars": 7660,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/DeclMetaTypes.swift",
    "chars": 6834,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/DeclRemover.swift",
    "chars": 4988,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/DeclVisitor.swift",
    "chars": 6669,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/ImportRewriter.swift",
    "chars": 1852,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Core/RefChecker.swift",
    "chars": 4183,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/FileParsers/DeclParser.swift",
    "chars": 6939,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/FileUpdaters/DeclUpdater.swift",
    "chars": 3684,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Operations/RemoveDeadDecls.swift",
    "chars": 15234,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Operations/RemoveUnusedImports.swift",
    "chars": 5158,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Operations/UpdateAccessLevels.swift",
    "chars": 16768,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Extensions/FileManagerExtensions.swift",
    "chars": 1137,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Extensions/SequenceExtensions.swift",
    "chars": 1743,
    "preview": "\n//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n// "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Extensions/StringExtensions.swift",
    "chars": 2505,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Extensions/SyntaxExtensions.swift",
    "chars": 42524,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Extensions/SyntaxParserExtensions.swift",
    "chars": 1525,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Logger.swift",
    "chars": 2551,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Sources/SwiftCodeSanKit/Utils/Scanner.swift",
    "chars": 4350,
    "preview": "//\n//  Copyright (c) 2018. Uber Technologies\n//\n//  Licensed under the Apache License, Version 2.0 (the \"License\");\n//  "
  },
  {
    "path": "Tests/SwiftCodeSanTestCase.swift",
    "chars": 3424,
    "preview": "import XCTest\nimport SwiftCodeSanKit\n\nclass SwiftCodeSanTestCase: XCTestCase {\n    var srcFilePathsCount = 1\n    var moc"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test0.swift",
    "chars": 450,
    "preview": "#if TESTFILE\nextension Integration {\n    var isUnitTest: Bool {\n        #if TEST\n        if case .test = RunType.current"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test1.swift",
    "chars": 2813,
    "preview": "#if TESTFILE\nimport Foundation\n\npublic protocol FileHandler {\n\n    func createFile(atPath: String, contents: Data?, attr"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test2.swift",
    "chars": 1598,
    "preview": "//\n//  Copyright © Uber Technologies, Inc. All rights reserved.\n//\n#if TESTFILE\n\nimport Foundation\n\n@_transparent public"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test3.swift",
    "chars": 750,
    "preview": "#if TESTFILE\n\n\nimport Foundation\n\npublic protocol ProtocolY {\n    func filterBy(uuid: String) -> Int?\n}\npublic protocol "
  },
  {
    "path": "Tests/TestClasses/Fixtures/test4.swift",
    "chars": 1082,
    "preview": "#if TESTFILE\n\n\nimport Foundation\n\npublic enum BarType {\n    case current\n}\n\n\nclass Klass: P {\n    private static var unu"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test5.swift",
    "chars": 1083,
    "preview": "#if TESTFILE\n\nenum State {\n    case eat(SomeFood)\n    case sleep(SomeTime)\n}\n\npublic func toJSON(arg: String) -> JSON {\n"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test6.swift",
    "chars": 1169,
    "preview": "#if TESTFILE\n\nimport Foundation\nimport Test1\n\n\nprivate let error = NSError(domain: TestDomain, code: InvalidData, userIn"
  },
  {
    "path": "Tests/TestClasses/Fixtures/test7.swift",
    "chars": 1633,
    "preview": "#if TESTFILE\n\nextension Foo {\n    #if DEBUG\n        var baz: Bool {\n            if case .test = RunType.current {\n      "
  },
  {
    "path": "Tests/TestClasses/Fixtures/test8.swift",
    "chars": 617,
    "preview": "#if TESTFILE\n\nimport Test9\nimport Test10\nimport Foundation\n\nclass Foo: ProtocolX {\n    func filterBy(uuid: String) -> In"
  },
  {
    "path": "Tests/TestClasses/SwiftCodeSanTests.swift",
    "chars": 130,
    "preview": "import Foundation\n\nclass DCETests: SwiftCodeSanTestCase {\n    \n    func testExample() {\n        // TODO: add a test\n    "
  },
  {
    "path": "install-script.sh",
    "chars": 1633,
    "preview": "#!/bin/bash\n\n# Causes the shell to exit if any subcommand returns a non-zero status\nset -e\n\nshowhelp() {\n    echo \"Descr"
  }
]

About this extraction

This page contains the full source code of the uber/SwiftCodeSan GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 40 files (185.8 KB), approximately 42.4k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!