{"id":13483500,"url":"https://github.com/sorbet/sorbet","last_synced_at":"2026-04-21T08:05:02.598Z","repository":{"id":37271052,"uuid":"138776049","full_name":"sorbet/sorbet","owner":"sorbet","description":"A fast, powerful type checker designed for Ruby","archived":false,"fork":false,"pushed_at":"2026-04-19T02:23:52.000Z","size":211583,"stargazers_count":3761,"open_issues_count":968,"forks_count":583,"subscribers_count":29,"default_branch":"master","last_synced_at":"2026-04-19T02:44:28.866Z","etag":null,"topics":["ruby","sorbet","types"],"latest_commit_sha":null,"homepage":"https://sorbet.org","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sorbet.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":"NOTICE","maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-06-26T18:13:06.000Z","updated_at":"2026-04-19T00:22:34.000Z","dependencies_parsed_at":"2024-01-22T20:40:59.258Z","dependency_job_id":"af16d3ed-6b9b-491b-86e6-e0a4a244c26b","html_url":"https://github.com/sorbet/sorbet","commit_stats":{"total_commits":11174,"total_committers":415,"mean_commits":"26.925301204819277","dds":0.8093789153391803,"last_synced_commit":"6e0707a80eaf49f5cfe95b28f2bc2bb0623f5525"},"previous_names":[],"tags_count":6445,"template":false,"template_full_name":null,"purl":"pkg:github/sorbet/sorbet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorbet%2Fsorbet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorbet%2Fsorbet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorbet%2Fsorbet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorbet%2Fsorbet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sorbet","download_url":"https://codeload.github.com/sorbet/sorbet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sorbet%2Fsorbet/sbom","scorecard":{"id":838434,"data":{"date":"2025-08-11","repo":{"name":"github.com/sorbet/sorbet","commit":"0c71823358c30b32849aad9a296fcd3443717b61"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":5,"checks":[{"name":"Maintained","score":10,"reason":"30 commit(s) and 18 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":10,"reason":"all changesets reviewed","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/ci.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: Apache License 2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact 0.5.12414.20250815171158-0c7182335 not signed: https://api.github.com/repos/sorbet/sorbet/releases/240334717","Warn: release artifact 0.5.12413.20250815164801-8148c3e18 not signed: https://api.github.com/repos/sorbet/sorbet/releases/240332198","Warn: release artifact 0.5.12412.20250815153750-8149023da not signed: https://api.github.com/repos/sorbet/sorbet/releases/240327758","Warn: release artifact 0.5.12411.20250815153419-9c1a1307e not signed: https://api.github.com/repos/sorbet/sorbet/releases/240327633","Warn: release artifact 0.5.12410.20250815145810-91288c74b not signed: https://api.github.com/repos/sorbet/sorbet/releases/240323975","Warn: release artifact 0.5.12414.20250815171158-0c7182335 does not have provenance: https://api.github.com/repos/sorbet/sorbet/releases/240334717","Warn: release artifact 0.5.12413.20250815164801-8148c3e18 does not have provenance: https://api.github.com/repos/sorbet/sorbet/releases/240332198","Warn: release artifact 0.5.12412.20250815153750-8149023da does not have provenance: https://api.github.com/repos/sorbet/sorbet/releases/240327758","Warn: release artifact 0.5.12411.20250815153419-9c1a1307e does not have provenance: https://api.github.com/repos/sorbet/sorbet/releases/240327633","Warn: release artifact 0.5.12410.20250815145810-91288c74b does not have provenance: https://api.github.com/repos/sorbet/sorbet/releases/240323975"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":10,"reason":"project is fuzzed","details":["Info: CppLibFuzzer integration found: test/fuzz/fuzz_dash_e.cc:56","Info: CppLibFuzzer integration found: test/fuzz/fuzz_doc_symbols.cc:52","Info: CppLibFuzzer integration found: test/fuzz/fuzz_dash_e.cc:56","Info: CppLibFuzzer integration found: test/fuzz/fuzz_doc_symbols.cc:52"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Info: Possibly incomplete results: error parsing shell code: \"foo(\" must be followed by ): gems/sorbet/bin/srb:0","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/ci.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/sorbet/sorbet/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:17: update your workflow using https://app.stepsecurity.io/secureworkflow/sorbet/sorbet/ci.yml/master?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/ci.yml:21: update your workflow using https://app.stepsecurity.io/secureworkflow/sorbet/sorbet/ci.yml/master?enable=pin","Info:   0 out of   1 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   2 third-party GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"43 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-gxpj-cx7g-858c","Warn: Project is vulnerable to: GHSA-2j2x-2gpw-g8fm","Warn: Project is vulnerable to: GHSA-6vfc-qv3f-vr6c","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-558p-m34m-vpmq","Warn: Project is vulnerable to: GHSA-cf4h-3jhx-xvhq","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-w8qv-6jwh-64r5","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-q8pj-2vqx-8ggc","Warn: Project is vulnerable to: GHSA-x3cc-x39p-42qx","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-pfrx-2q88-qq97","Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq","Warn: Project is vulnerable to: GHSA-rc47-6667-2j5j","Warn: Project is vulnerable to: GHSA-33f9-j839-rf8h","Warn: Project is vulnerable to: GHSA-c36v-fmgq-m8hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-76p3-8jx3-jpfq","Warn: Project is vulnerable to: GHSA-3rfm-jhwj-7488","Warn: Project is vulnerable to: GHSA-hhq3-ff78-jv3g","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-rp65-9cf3-cjxr","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-x7hr-w5r2-h6wg","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-44c6-4v22-4mhx","Warn: Project is vulnerable to: GHSA-4x5v-gmq8-25ch","Warn: Project is vulnerable to: GHSA-g4rg-993r-mgx7","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-7p7h-4mm5-852v"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T19:48:42.396Z","repository_id":37271052,"created_at":"2025-08-23T19:48:42.396Z","updated_at":"2025-08-23T19:48:42.396Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32052534,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-20T11:35:06.609Z","status":"ssl_error","status_checked_at":"2026-04-20T11:34:48.899Z","response_time":94,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ruby","sorbet","types"],"created_at":"2024-07-31T17:01:12.016Z","updated_at":"2026-04-21T08:05:02.579Z","avatar_url":"https://github.com/sorbet.png","language":"C++","funding_links":[],"categories":["Ruby","Code Analysis and Metrics","C++"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"Sorbet logo\" width=\"200\" src=\"docs/logo/sorbet-logo-purple-sparkles.svg\"\u003e\n\u003c/p\u003e\n\n# Sorbet\n\nThis repository contains Sorbet, a fast, powerful type checker designed for Ruby.\nIt aims to be easy to add to existing codebases with gradual types, and fast to\nrespond with errors and suggestions.\n\nThis README contains documentation specifically for contributing to Sorbet. You\nmight also want to:\n\n- Read the [public Sorbet docs](https://sorbet.org/docs/overview)\n  - Or even [edit the docs](#writing-docs)\n- Watch the [talks we've given](https://sorbet.org/en/community#talks) about Sorbet\n- Try the [Sorbet playground](https://sorbet.run) online\n\nIf you are at Stripe, you might also want to see \u003chttp://go/types/internals\u003e for\ndocs about Stripe-specific development workflows and historical Stripe context.\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n## Table of Contents\n\n- [Sorbet user-facing design principles](#sorbet-user-facing-design-principles)\n- [Quickstart](#quickstart)\n- [Learning how Sorbet works](#learning-how-sorbet-works)\n- [Building Sorbet](#building-sorbet)\n  - [Common Compilation Errors](#common-compilation-errors)\n- [Running Sorbet](#running-sorbet)\n- [Running the tests](#running-the-tests)\n- [Testing Sorbet against pay-server](#testing-sorbet-against-pay-server)\n- [Writing tests](#writing-tests)\n  - [test_corpus tests](#test_corpus-tests)\n  - [Expectation tests](#expectation-tests)\n  - [CLI tests](#cli-tests)\n  - [LSP tests](#lsp-tests)\n    - [Testing \"Find Definition\" and \"Find All References\"](#testing-find-definition-and-find-all-references)\n    - [Testing \"Go to Type Definition\"](#testing-go-to-type-definition)\n    - [Testing hover](#testing-hover)\n    - [Testing completion](#testing-completion)\n    - [Testing workspace symbols (symbol search)](#testing-workspace-symbols-symbol-search)\n    - [Testing \"Go to Implementation\"](#testing-go-to-implementation)\n    - [Testing rename constant](#testing-rename-constant)\n    - [Testing incremental type checking](#testing-incremental-type-checking)\n  - [LSP recorded tests](#lsp-recorded-tests)\n  - [Updating tests](#updating-tests)\n- [Debugging](#debugging)\n- [Writing docs](#writing-docs)\n- [Editor and environment](#editor-and-environment)\n  - [Bazel](#bazel)\n  - [Shell](#shell)\n  - [Formatting files](#formatting-files)\n  - [Editor setup for C++](#editor-setup-for-c)\n  - [Editor setup for test/testdata](#editor-setup-for-testtestdata)\n    - [@jez setup](#jez-setup)\n    - [others](#others)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Sorbet user-facing design principles\n\nEarly in our project, we've defined some guidelines for how working with Sorbet should feel like.\n\n1. **Explicit**\n\n    We're willing to write annotations, and in fact see them as\n    beneficial; they make code more readable and predictable. We're here\n    to help readers as much as writers.\n\n2. **Feel useful, not burdensome**\n\n    While it is explicit, we are putting effort into making it concise.\n    This shows in multiple ways:\n     - error messages should be clear\n     - verbosity should be compensated with more safety\n\n3. **As simple as possible, but powerful enough**\n\n    Overall, we are not strong believers in super-complex type\n    systems. They have their place, and we need a fair amount of\n    expressive power to model (enough) real Ruby code, but all else\n    being equal we want to be simpler. We believe that such a system\n    scales better, and—most importantly—is easier for our users to\n    learn \u0026 understand.\n\n4. **Compatible with Ruby**\n\n    In particular, we don't want a new syntax. Existing Ruby syntax means\n    we can leverage most of our existing tooling (editors, etc). Also,\n    the point of Sorbet is to gradually improve an existing Ruby codebase. No\n    new syntax makes it easier to be compatible with existing tools.\n\n5. **Scales**\n\n    On all axes: execution speed, number of collaborators, lines of code,\n    codebase age. We work in large Ruby codebases, and they will only get\n    larger.\n\n6. **Can be adopted gradually**\n\n    In order to make adoption possible at scale, we cannot require every team or\n    project to adopt Sorbet all at once. Sorbet needs to support teams adopting\n    it at different paces.\n\n## Quickstart\n\n1.  Install the dependencies\n\n    - `brew install bazel autoconf coreutils parallel`\n\n2.  Clone this repository\n\n    - `git clone https://github.com/sorbet/sorbet.git`\n    - `cd sorbet`\n\n3.  Build Sorbet\n\n    - `./bazel build //main:sorbet --config=dbg`\n\n4.  Run Sorbet!\n\n    - `bazel-bin/main/sorbet -e \"42 + 'hello'\"`\n\n\n## Learning how Sorbet works\n\nWe've documented the [internals of Sorbet](docs/internals.md) in a separate doc.\nCross-reference between that doc and here to learn how Sorbet works and how to\nchange it!\n\n[→ internals.md](docs/internals.md)\n\nThere is also a talk online that describes Sorbet's high-level architecture and\nthe reasons why it's fast:\n\n[→ Fast type checking for Ruby](https://sorbet.org/docs/talks/jvm-ls-2019)\n\n\n## Building Sorbet\n\nThere are multiple ways to build `sorbet`. This one is the most common:\n\n```\n./bazel build //main:sorbet --config=dbg\n```\n\nThis will build an executable in `bazel-bin/main/sorbet` (see \"Running Sorbet\"\nbelow). There are many options you can pass when building `sorbet`:\n\n- `--config=dbg`\n  - Most common build config for development.\n  - Good stack traces, runs all ENFORCEs.\n- `--config=sanitize`\n  - Link in extra sanitizers, in particular: UBSan and ASan.\n  - Catches most memory and undefined-behavior errors.\n  - Substantially larger and slower binary.\n- `--config=debugsymbols`\n  - (Included by `--config=dbg`) debugging symbols, and nothing else.\n- `--config=forcedebug`\n  - Use more memory, but report even more sanity checks.\n- `--config=static-libs`\n  - Forcibly use static linking (Sorbet defaults to dynamic linking for faster\n    build times).\n  - Sorbet already uses this option in release builds (see below).\n- `--config=release-mac` and `--config=release-linux`\n  - Exact release configuration that we ship to our users.\n\nIndependently of providing or omitting any of the above flags, you can turn on\noptimizations for any build:\n\n- `-c opt`\n  - Enables `clang` optimizations (i.e., `-O2`)\n\nThese args are not mutually exclusive. For example, a common pairing when\ndebugging is\n\n```\n--config=dbg --config=sanitize\n```\n\nIn `.bazelrc` you can find out what all these options (and others) mean.\n\n### Common Compilation Errors\n\n**(Mac) `Xcode version must be specified to use an Apple CROSSTOOL`**\n\nThis error typically occurs after an Xcode upgrade.\n\nDeveloper tools must be installed, the Xcode license must be accepted, and\nyour active Xcode command line tools directory must point to an installed\nversion of Xcode.\n\nThe following commands should do the trick:\n\n```shell\n# Install command line tools\nxcode-select --install\n# Ensure that the system finds command line tools in an active Xcode directory\nsudo xcode-select -s /Applications/Xcode.app/Contents/Developer\n# Accept the Xcode license.\nsudo xcodebuild -license\n# Clear bazel's cache, which may contain files generated from a previous\n# version of Xcode command line tools.\nbazel clean --expunge\n```\n\n**(Mac) `fatal error: 'math.h' file not found`** (or some other system header)\n\nThis error can happen on Macs when the `/usr/include` folder is missing. The\nsolution is to install macOS headers via the following package:\n\nmacOS Mojave:\n```shell\nopen /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg\n```\n\nmacOS Catalina:\n```shell\nsudo ln -s /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/* /usr/local/include/\n```\n\n## Running Sorbet\n\nRun Sorbet on an expression:\n\n```\nbazel-bin/main/sorbet -e \"1 + false\"\n```\n\nRun Sorbet on a file:\n\n```\nbazel-bin/main/sorbet foo.rb\n```\n\nRunning `bazel-bin/main/sorbet --help` will show lots of options. These are\nthe common ones for contributors:\n\n\n- `-p \u003cIR\u003e`\n  - Asks sorbet to print out any given intermediate representation.\n  - See `--help` for available values of `\u003cIR\u003e`.\n- `--stop-after \u003cphase\u003e`\n  - Useful when there's a bug in a later phase, and you want to quit early to\n    debug.\n- `-v`, `-vv`, `-vvv`\n  - Show `logger` output (increasing verbosity)\n- `--max-threads=1`\n  - Useful for determining if you're dealing with a concurrency bug or not.\n- `--wait-for-dbg`\n  - Will freeze Sorbet on startup and wait for a debugger to attach\n  - This is useful when you don't have control over launching the process (LSP)\n\n\n## Running the tests\n\nTo run all the tests:\n\n```\nbazel test //... --config=dbg\n```\n\n(The `//...` literally means \"all targets\".)\n\nTo run a subset of the tests curated for faster iteration and development speed,\nrun:\n\n```\nbazel test test --config=dbg\n```\n\nNote that in bazel terms, the second test is an alias for `//test:test`, so we're being a bit cute here.\n\nBy default, all test output goes into files. To also print it to the screen:\n\n```\nbazel test //... --config=dbg --test_output=errors\n```\n\nIf any test failed, you will see two pieces of information printed:\n\n```\n1. //test:test_testdata/resolver/optional_constant\n2.   /private/var/tmp/.../test/test_testdata/resolver/optional_constant/test.log\n```\n\n1.  the test's target (in case you want to run just this test again with `bazel\n    test \u003ctarget\u003e`)\n2.  a (runnable) file containing the test's output\n\nTo see the failing output, either:\n\n- Re-run `bazel test` with the `--test_output=errors` flag\n- Copy/paste the `*.log` file and run it (the output will open in `less`)\n\n\n## Testing Sorbet against pay-server\n\n\u003e This is specific to contributing to Sorbet at Stripe.\n\nIf you are at Stripe and want to test your branch against pay-server, see\n\u003chttp://go/types/local-dev\u003e.\n\n## Writing tests\n\nWe write tests by adding files to subfolders of the `test/` directory.\nIndividual subfolders are \"magic\"; each contains specific types of tests.\nWe aspire to have our tests be fully reproducible.\n\n\u003e **C++ note**: In C++, hash functions are only required to produce the same\n\u003e result for the same input within a single execution of a program.\n\u003e\n\u003e Thus, we expect all user-visible outputs to be explicitly sorted using a\n\u003e key stable from one run to the next.\n\nThere are many ways to test Sorbet, some \"better\" than others. We've ordered\nthem below in order from most preferable to least preferable. And we always\nprefer some tests to no tests!\n\n### test_corpus tests\n\nThe first kind of test can be called either [test_corpus] tests or [testdata]\ntests, based on the name of the test harness or the folder containing these tests, respectively.\n\n\n[test_corpus]: test/pipeline_test_runner.cc\n[testdata]: test/testdata/\n\nTo create a test_corpus test, add any file `\u003cname\u003e.rb` to `test/testdata`, in\nany folder depth. The file must either:\n\n- type check entirely, or\n- throw errors **only** on lines marked with a comment (see below).\n\nTo mark that a line should have errors, append `# error: \u003cmessage\u003e` (the\n`\u003cmessage\u003e` must match the raised error message). In case there are multiple\nerrors on this line, add an `# error: \u003cmessage\u003e` on its own line just below.\n\nError checks can optionally point to a range of characters rather than a line:\n\n```ruby\n1 + '' # error: `String` doesn't match `Integer`\n\nrescue Foo, Bar =\u003e baz\n     # ^^^ error: Unable to resolve constant `Foo`\n          # ^^^ error: Unable to resolve constant `Bar`\n```\n\nYou can run this test with:\n\n```\nbazel test //test:test_PosTests/testdata/path/to/\u003cname\u003e\n```\n\n### Expectation tests\n\nEach test_corpus test can be turned into an expectation test by optionally\ncreating any number of `\u003cname\u003e.rb.\u003cphase\u003e.exp` files (where `\u003cname\u003e` matches the\nname of the ruby file for this test). These files contain pretty printed\nrepresentations of internal data structures, according to what would be printed\nby `-p \u003cphase\u003e`. The snapshot must exactly match the output generated by running\n`sorbet -p \u003cphase\u003e \u003cname\u003e.rb` for the test to pass.\n\nYou can run this test with:\n\n```\nbazel test //test:test_PosTests/testdata/path/to/\u003cname\u003e\n```\n\nFiles that begin with a prefix and `__` will be run together. For example,\n`foo__1.rb` and `foo__2.rb` will be run together as test `foo`. If such sets of\nfiles have `*.exp` files associated with them, the `*.exp` files must instead\nfollow the pattern `\u003cname\u003e.\u003cphase\u003e.exp`, where `\u003cname\u003e` does not include the\n`__*.rb` suffix. So `foo__1.rb` and `foo__2.rb` would have an exp file like\n`foo.\u003cpass\u003e.exp`.\n\nAnother exception: for `package-tree` exp tests, the filename is always\n`pass.package-tree.exp`, no matter the name of the test.\n\n### CLI tests\n\nAny folder `\u003cname\u003e` that is added to `test/cli/` becomes a test.\nThis folder should have a file `test.sh` that is executable.\nWhen run, its output will be compared against `test.out` in that folder.\n\nOur bazel setup will produce two targets:\n\n- `bazel run //test/cli:test_\u003cname\u003e` will execute the `.sh` file\n- `bazel test //test/cli:test_\u003cname\u003e` will execute the `.sh` and check it against what's in\n  the `.out` file.\n\nThe scripts are run inside Bazel, so they will be executed from the top of the\nworkspace and have access to source files and built targets using their path\nfrom the root. In particular, the compiled sorbet binary is available under\n`main/sorbet`.\n\n### LSP tests\n\nMost LSP tests are expectation tests with additional LSP-specific annotations.\nThey are primarily contained in `test/testdata/lsp`, but all files in `test/testdata`\nare tested in LSP mode. You can run a test `test/testdata/lsp/\u003cname\u003e.rb` like so:\n\n```\nbazel test //test:test_LSPTests/testdata/lsp/\u003cname\u003e\n```\n\n#### Testing \"Find Definition\" and \"Find All References\"\n\nLSP tests have access to `def` and `usage` assertions that you can use to annotate definition\nand usage sites for a variable:\n\n```ruby\n  a = 10\n# ^ def: a\n  b = a + 10\n    # ^ usage: a\n```\n\nWith these annotations, the test will check that \"Find Definition\" from the addition will lead to\n`a = 10`, and that \"Find All References\" from either location will return both the definition and usage.\n\nIf a variable is re-defined, it can be annotated with a version number:\n\n```ruby\n  a = 10\n# ^ def: a 1\n  a = 20\n# ^ def: a 2\n  b = a + 10\n    # ^ usage: a 2\n```\n\n`usage` annotations can accept multiple version numbers, separated by a `,`. This is useful if you have variables that\nget re-defined through multiple-paths:\n\n```ruby\n  if some_condition\n    a = 10\n  # ^ def a 1\n  else\n    a = 'hello'\n  # ^ def: a 2\n  end\n\n  p a\n  # ^ usage: a 1,2\n```\n\nIf a location should not report any definition or usage, then use the magic label `(nothing)`:\n\n```ruby\n    a = 10\n# ^ def: (nothing)\n```\n\nIf a location should report multiple definitions (e.g., a class or module opened\nin multiple files), then you can add a second `def` with the same name:\n\n```ruby\nclass Foo\n  #   ^^^ def: foo\nend\n\nclass Foo\n  #   ^^^ def: foo\nend\n```\n\nWhen marking definitions that correspond to method arguments that have defaults, multiple definitions will need to be\nmarked: one for the argument definition itself and one for its default value. The default value needs to be given a\ndifferent version number, and also marked `default-arg-value`:\n\n```ruby\n  def foo(a: 1)\n        # ^ def: a 1\n           # ^ def: a 2 default-arg-value\n    p a\n    # ^ usage: a 1,2\n  end\n```\n\nThis is due to the translation of defaults into the CFG: there is a synthetic conditional that chooses either to\ninitialize the variable from the argument passed at the send, or to the default value when no value is present.\n\nFinding all references works differently in package specification (`__package.rb`) files. Consider the following:\n\n```ruby\nclass Foo \u003c PackageSpec\n  import Bar\n```\n\nCalling \"find all references\" on `Bar` in this file will return only references to `Bar` in the `Foo` package. LSP tests\nhave access to `import` and `importusage` assertions that you can use to test this functionality.\n\n```ruby\nclass Foo \u003c PackageSpec\n  import Bar\n  #      ^^^ import: bar\n```\n\n```ruby\n  class Foo::Baz\n    Bar.new\n #  ^^^ importusage: bar\n  end\n```\n\nWith these annotations, the LSP test will check if \"find all references\" on `Bar` in `import Bar` statement returns the `Bar.new` usage.\n\nNote that an `import` assertion is dissimilar to a `def` assertion, in that it is in fact a subclass of a `usage` assertion.\nIn this case, the `def` corresponding to an `import` is the PackageSpec declaration of the imported package. Calling \"find all references\"\non a PackageSpec declaration will return all imports of the package.\n\n\n```ruby\nclass Bar \u003c PackageSpec\n  #   ^^^ def: bar\n  import Bar\n```\n\n```ruby\nclass Foo \u003c PackageSpec\n  import Bar\n  #      ^^^ import: bar\n```\n\n```ruby\nclass Baz \u003c PackageSpec\n  import Bar\n  #      ^^^ import: bar\n```\n\nWith these annotations, the LSP test will check if \"find all references\" on `Bar` from the `class Bar \u003c PackageSpec`\ndeclaration returns the declaration itself plus the imports.\n\n#### Testing \"Go to Type Definition\"\n\nThis is somewhat similar to \"Find Definition\" above, but also slightly different\nbecause there's no analogue of \"Find All Type Definitions.\"\n\n```ruby\nclass A; end\n#     ^ type-def: some-label\n\naaa = A.new\n# ^ type: some-label\n```\n\nThe `type: some-label` assertion says \"please simulate a Go to Type Definition\nhere, named `some-label`\" and the `type-def: some-label` assertion says \"assert\nthat the results for `some-label` are exactly these locations.\"\n\nThat means if the type definition could return multiple locs, the assertions\nwill have to cover all results:\n\n```ruby\nclass A; end\n#     ^ type-def: AorB\nclass B; end\n#     ^ type-def: AorB\n\naaa = T.let(A.new, T.any(A, B))\n# ^ type: AorB\n```\n\nIf a location should not report any definition or usage, then use the magic\nlabel `(nothing)`:\n\n```ruby\n# typed: false\nclass A; end\naaa = A.new\n# ^ def: (nothing)\n```\n\n#### Testing hover\n\nLSP tests can also assert the contents of hover responses with `hover` assertions:\n\n```ruby\n  a = 10\n# ^ hover: Integer(10)\n```\n\nIf a location should report the empty string, use the special label `(nothing)`:\n\n```ruby\n     a = 10\n# ^ hover: (nothing)\n```\n\nAssert the contents of a specific line of the hover response with `hover-line` assertions:\n\n```ruby\n  a = 10\n# ^ hover-line: 1 Integer(10)\n```\n\n#### Testing completion\n\nLSP tests can also assert the contents of completion responses with `completion`\nassertions.\n\n```ruby\nclass A\n  def self.foo_1; end\n  def self.foo_2; end\n\n  foo\n#    ^ completion: foo_1, foo_2\nend\n```\n\nThe `^` corresponds to the position of the cursor. So in the above example, it's\nas if the cursor is like this: `foo│`. If the `^` had been directly under the\nlast `o`, it would have been like this: `fo|o`. Only the first `^` is used. If\nyou use `^^^` in the assertion, the test harness will send a completion\nassertion at the position of the first caret.\n\nYou can also write a test for a partial prefix of the completion results:\n\n```ruby\nclass A\n  def self.foo_1; end\n  def self.foo_2; end\n\n  foo\n#    ^ completion: foo_1, ...\nend\n```\n\nAdd the `, ...` suffix to the end of a partial list of completion results, and\nthe test harness will ensure that the listed identifiers match a prefix of the\ncompletion items. This prefix must still be listed in order.\n\nIf a location should report zero completion items, use the special message\n`(nothing)`:\n\n```ruby\nclass A\n  def self.foo_1; end\n  def self.foo_2; end\n\n  zzz\n#    ^ completion: (nothing)\nend\n```\n\nTo write a test for the snippet that would be inserted into the document if a\nparticular completion item was selected, you can make two files:\n\n```\n# -- test/testdata/lsp/completion/mytest.rb --\nclass A\n  def self.foo_1; end\nend\n\nA.foo_\n#     ^ apply-completion: [A] item: 0\n```\n\nThe `apply-completion` assertion says \"make sure the file `mytest.A.rbedited`\ncontains the result of inserting the completion snippet for the 0th completion\nitem into the file.\"\n\n```\n# -- test/testdata/lsp/completion/mytest.A.rbedited --\nclass A\n  def self.foo_1; end\nend\n\nA.foo_1${0}\n#     ^ apply-completion: [A] item: 0\n```\n\nAs you can see, the fancy `${...}` (tabstop placeholders) show up verbatim in\nthe output if they were sent in the completion response.\n\nIt's not currently possible to test these parts of a completion response:\n\n- completion kind\n- documentation\n- detail\n\nFor these, your best bet is to test manually in VS Code / your preferred editor\nand verify that you're seeing your changes. For documentation specifically,\nnearly all the code paths are shared with hover, so you can alternatively write\na hover test.\n\n#### Testing workspace symbols (symbol search)\n\nLSP tests can assert that a specific item appears in a symbol search (the\n`textDocument/workspaceSymbols` request) using the `symbol-search` assertion:\n\n```ruby\nclass Project::Foo\n#     ^^^ symbol-search: \"Foo\"\nend\n```\n\nThe `symbol-search` can optionally specify _how_ that item should appear in\nsearch results:\n\n```ruby\nclass Project::Foo\n#     ^^^ symbol-search: \"Foo\", name = \"Foo\", container = \"Project\"\nend\n```\n\nIn the above, `container` can also be the special string `\"(nothing)\"` to\nindicate that the item has no container.\n\n`symbol-search` can also specify the item's relative rank in the ordered\nsearch results:\n\n```ruby\nclass Project::Foo\n#     ^^^ symbol-search: \"Foo\", rank = 1\nend\n```\n\n#### Testing \"Go to Implementation\"\n\nTesting the \"Go to Implementation\" feature is really similar to the testing techniques of the \"Go to Type Definition\".\n\n```ruby\nmodule A\n#      ^ find-implementation: A\n  extend T::Sig\n  extend T::Helpers\n  interface!\nend\n\n class B\n#^^^^^^^ implementation: A\n  extend T::Sig\n  include A\n#         ^ find-implementation: A\nend\n```\n\nThere are two types of assertions:\n\n1. `find-implementation: \u003csymbol\u003e` means make a \"Go to Implementation\" request here. `\u003csymbol\u003e` marks the symbol name we are looking for.\n2. `implementation: \u003csymbol\u003e` marks the location which should be returned for the \"Go to Implementation\" call for a given `\u003csymbol\u003e`\n\nIf the request returns multiple locations, you should mark all of them with `implementation: \u003csymbol\u003e`\n\n#### Testing rename constant\n\nTo write a test for renaming constants, you need to make at least two files:\n\n```ruby\n# -- test/testdata/lsp/refactor/mytest.rb --\n\n# typed: true\n# frozen_string_literal: true\n\nclass Foo\n  class Foo\n  end\nend\n\nfoo = Foo.new\n#     ^ apply-rename: [A] newName: Bar\n```\n\nThe `apply-rename` assertion here says \"simulate a user starting a rename from\nthe position of this caret.\" You'll need to add an `.rbedited` file that reflects\nwhat the result of the changes should look like. In this case, the file would look\nlike this:\n\n```ruby\n# -- test/testdata/lsp/refactor/mytest.A.rbedited --\n\n# typed: true\n# frozen_string_literal: true\n\nclass Bar\n  class Foo\n  end\nend\n\nfoo = Bar.new\n#     ^ apply-rename: [A] newName: Bar\n```\n\nYou can test that invalid renames aren't applied by adding `invalid: true` to your\ntest, like so:\n```ruby\n# -- test/testdata/lsp/refactor/mytest.rb --\n\n# typed: true\n# frozen_string_literal: true\n\nclass Foo\n  class Foo\n  end\nend\n\nfoo = Foo.new\n#     ^ apply-rename: [A] newName: foo invalid:true\n```\n\nTo test for a specific error message, add an `expectedErrorMessage` argument to the test:\n```ruby\n# typed: true\n# frozen_string_literal: true\n\nrequire_relative './constant__class_definition.rb'\n\nsig { params(foo: Foo::Foo).returns(Foo::Foo) }\ndef foo(foo); end\n\nclass Baz\n#     ^ apply-rename: [D] newName: Bar invalid: true expectedErrorMessage: Renaming constants defined in .rbi files is not supported; symbol Baz is defined at test/testdata/lsp/rename/constant__rbi_class_reference.rbi\n\nend\n```\n\nYou can add more files that reference the constant you're renaming, just make sure\nto add a matching `.rbedited` file with the same version.\n\n#### Testing incremental type checking\n\nIn LSP mode, Sorbet runs file updates on a *fast path* or a *slow path*. It checks the structure of the\nfile before and after the update to determine if the change is covered under the fast path. If it is,\nit performs further processing to determine the set of files that need to be type checked.\n\nLSP tests can define file updates in `\u003cname\u003e.\u003cversion\u003e.rbupdate` files which contain the contents of `\u003cname\u003e.rb`\nafter the update occurs. For example, the file `foo.1.rbupdate` contains the updated contents of `foo.rb`.\n\nIf the test contains multiple files by using a `__` suffixed prefix, then all rbupdates with the same version will\nbe applied in the same update. For example, `foo__bar.1.rbupdate` and `foo__baz.1.rbupdate` will be applied\nsimultaneously to update `foo__bar.rb` and `foo__baz.rb`.\n\nInside `*.rbupdate` files, you can assert that the slow path ran by adding a line with `# assert-slow-path: true`.\nYou can assert that the fast path ran on `foo__bar.rb` and `foo__baz.rb` with\n`#assert-fast-path: foo__bar.rb,foo__baz.rb`.\n\nNote that the default behavior when testing multi-file updates (e.g.,\n`*__1.1.rbupdate` + `*__2.1.rbupdate`) is to include all the files in the file\nupdate that is created and sent to the LSP server. When testing changes that\nassert whether the right files were typechecked on the fast path with\n`assert-fast-path`, you also likely want to declare which files **should not**\nbe included in the file edit, leaving Sorbet to figure out the subset of files\nto be typechecked. **But** regardless of whether a file was included in the\nupdate set, you likely want to assert that error occur at certain points inside\nthe file. For this, you can use `# exclude-from-file-update: true` inside an\n`rbupdate` file. Note that when using this, the act of adding the\n`exclude-from-file-update` assertion in the `rbupdate` will have the effect of\nshifting all the `error` assertions off by one line compared to where the LSP\nserver will be reporting those errors. To work around this, you should leave a\nspacer line in the previous file, so that the `exclude-from-file-update`\nassertion replaces the spacer line, instead of being inserted into the file as a\ncompletely new line. Search for `spacer` in some of the `fast_path` tests to see\nan example.\n\nTo craft an update to an RBI file, use `.rbiupdate` instead of `.rbupdate`,\nunless you mean to simulate the effect of converting an RBI file to an RB file.\n\n#### Testing `sorbet/hierarchyReferences`\n\nThere are two modes of testing `sorbet/hierarchyReferences`:\n\n- `^ hierarchy-ref-set: \u003csymbol\u003e`\n\n  Making a `sorbet/hierarchyReferences` request from each location in the set\n  specified by `\u003csymbol\u003e` should include a list of Locations for all other\n  entries in the set.\n\n- `^ find-hierarchy-refs: \u003csymbol\u003e` + `^ hierarchy-ref: \u003csymbol\u003e`\n\n  Making a `sorbet/hierarchyReferences` request at each of the location\n  specified by the `find-hierarchy-refs` locations only should return a list of\n  Locations that includes both the original `find-hierarchy-refs` position and\n  any others specified by `hierarchy-ref`.\n\nPrefer the \"set\" version, because it is more concise and tests more things.\n\nUse the `find-hierarchy-refs` version when there is intentional asymmetry. For\nexample, given multiple inheritance, the hierarchy of two parent modules might\nbe unrelated, but the hierarchy of their shared child will include both of them.\nIn such cases, only `find-hierarchy-refs` can be used.\n\n### LSP recorded tests\n\nIt is possible to record an LSP session and use it as a test. We are attempting to move away from this form of\ntesting, as these tests are hard to update and understand. If at all possible, try to add your test case as a\nregular LSP test.\n\nAny folder `\u003cname\u003e` that is added to `test/lsp/` will become a test.\nThis folder should contain a file named `\u003cfolderName\u003e.rec` that contains a\nrecorded LSP session.\n\n- Lines that start with \"Read:\" will be sent to sorbet as input.\n- Lines that start with \"Write:\" will be expected from sorbet as output.\n\n### Updating tests\n\nFrequently when a test is failing, it's because something inconsequential\nchanged in the captured output, rather than there being a bug in your code.\n\nTo recapture the traces, you can run\n\n```\ntools/scripts/update_exp_files.sh\n```\n\nYou will probably want to look through the changes and `git checkout` any files\nwith changes that you believe are actually bugs in your code and fix your code.\n\n`update_exp_files.sh` updates every snapshot file kind known to Sorbet. This can\nbe slow, depending on what needs to be recompiled and updated. Some faster\ncommands:\n\n```bash\n# Only update the `*.exp` files in `test/testdata`\ntools/scripts/update_testdata_exp.sh\n\n# Only update the `*.exp` files in `test/testdata/cfg`\ntools/scripts/update_testdata_exp.sh test/testdata/cfg\n\n# Only update a single exp file's test:\ntools/scripts/update_testdata_exp.sh test/testdata/cfg/next.rb\n\n# Only update the `*.out` files in `test/cli`\nbazel test //test/cli:update\n```\n\n\n## Debugging\n\nIn general,\n\n- to debug a normal build of sorbet?\n  - `lldb bazel-bin/main/sorbet -- \u003cargs\u003e ...`\n  - (Consider using `--config=static-libs` for better debug symbols)\n  - If you see weird Python errors on macOS, try `PATH=/usr/bin lldb`.\n- to debug an existing Sorbet process (i.e., LSP)\n  - launch Sorbet with the `--wait-for-dbg` flag\n  - `lldb -p \u003cpid\u003e`\n  - set breakpoints and then `continue`\n\nAlso, it’s good to get in the practice of fixing bugs by first adding an\n`ENFORCE` (assertion) that would have caught the bug before actually fixing the\nbug. It’s far easier to fix bugs when there’s a nice error message stating what\ninvariant you’ve violated. `ENFORCE`s are free in the release build.\n\n## Writing docs\n\nThe sources for Sorbet's documentation website live in the\n[`website/`](website/) folder. Specifically, the docs live in\n[`website/docs/`](website/docs/), are all authored with Markdown, and are built\nusing [Docusaurus](https://docusaurus.io/).\n\n[→ website/README.md](website/README.md)\n\n^ See here for how to work with the documentation site locally.\n\n\n## Editor and environment\n\n### Bazel\n\nBazel supports having a persistent cache of previous build results so that\nrebuilds for the same input files are fast. To enable this feature, run this\nscript to create a `./.bazelrc.local` and cache folder:\n\n```shell\ntools/create_local_bazelrc.sh\n```\n\n### Shell\n\nMany of the build commands are very long. You might consider shortening the\ncommon ones with shell aliases of your choice:\n\n```shell\n# mnemonic: 's' for sorbet\nalias sb=\"bazel build //main:sorbet --config=dbg\"\nalias st=\"bazel test //... --config=dbg --test_output=errors\"\n```\n\n### Formatting files\n\nWe ensure that C++ files are formatted with `clang-format` and that Bazel BUILD\nfiles are formatted with `buildifier`. To avoid inconsistencies between\ndifferent versions of these tools, we have scripts which download and run these\ntools through `bazel`:\n\n```\ntools/scripts/format_cxx.sh\ntools/scripts/format_build_files.sh\n```\n\nCI will fail if there are any unformatted files, so you might want to set up\nyour files to be formatted automatically with one of these options:\n\n1.  Set up a pre-commit / pre-push hook which runs these scripts.\n2.  Set up your editor to run these scripts. See below.\n\n\n### Editor setup for C++\n\nThe `clang` suite of tools has a pretty great story around editor tooling: you\ncan build a `compile_commands.json` using Clang's [Compilation Database] format.\n\nMany clang-based tools consume this file to provide language-aware features in,\nfor example, editor integrations.\n\nTo build a `compile_commands.json` file for Sorbet with bazel:\n\n```\ntools/scripts/build_compilation_db.sh\n```\n\nThis builds a `./compile_commands.json` file (that is gitignored). This file\nhard-codes some paths into the Bazel sandbox. These files can get stale,\nespecially when they're generated by Bazel `genrule`'s. In particular, the\n`./compile_commands.json` references files in Bazel's `opt` configuration (e.g.,\nwhatever was last built with `-c opt` / `--compilation_mode=opt`). If you're\nseeing stale errors, consider running a command like `./bazel build\n//main:sorbet -c opt`.\n\nYou are encouraged to play around with various clang-based tools which use the\n`compile_commands.json` database. Some suggestions:\n\n-   [rtags] -- Clang aware jump-to-definition / find references / etc.\n\n    ```shell\n    brew install rtags\n\n    # Have the rtags daemon be automatically launched by macOS on demand\n    brew services start rtags\n\n    # cd into sorbet\n    # ensure that ./compile_commands.json exists\n\n    # Tell rtags to index sorbet using our compile_commands.json file\n    rc -J .\n    ```\n\n    There are rtags editor plugins for most text editors.\n\n-   [clangd] -- Clang-based language server implementation\n\n    `clangd` supports more features than `rtags` (specifically, reporting\n    Diagnostics), but can be somewhat slower at times because it does not\n    pre-index all your code like rtags does.\n\n    After successfully compiling Sorbet, point your editor to use the\n    `clangd` executable located in\n    `bazel-sorbet/external/llvm_toolchain_15_0_7/bin/clangd`.\n\n-   [clang-format] -- Clang-based source code formatter\n\n    We build `clang-format` in Bazel to ensure that everyone uses the same\n    version. Here's how you can get `clang-format` out of Bazel to use it in\n    your editor:\n\n    ```shell\n    # Build clang-format with bazel\n    ./bazel build //tools:clang-format\n\n    # Once bazel runs again, this symlink to clang-format will go away.\n    # We need to copy it out of bazel so our editor can use it:\n    mkdir -p \"$HOME/bin\"\n    cp bazel-bin/tools/clang-format $HOME/bin\n\n    # (Be sure that $HOME/bin is on your PATH, or use a path that is)\n    ```\n\n    With `clang-format` on your path, you should be able to find an editor\n    plugin that uses it to format your code on save.\n\n    Note: our format script passes some extra options to `clang-format`.\n    Configure your editor to pass these options along to `clang-format`:\n\n    ```shell\n    -style=file -assume-filename=\u003cCURRENT_FILE\u003e\n    ```\n\n-   [CLion] -- JetBrains C/C++ IDE\n\n    CLion can be made aware of the `compile_commands.json` database.\n    Replaces your entire text editing workflow (full-fledged IDE).\n\n-   [vscode-clangd] -- Clangd extension for VS Code\n\n    This extension integrates clangd (see above) with VS Code. It will also\n    run `clang-format` whenever you save. **Note: Microsoft's C/C++ extension\n    does *not* work properly with Sorbet's `compile_commands.json`.**\n\n    The settings for this repository automatically configure vscode-clangd to\n    run the clangd executable in the `bazel-sorbet` directory. Note that you\n    will need to compile Sorbet once before it will work.\n\n    clangd operates on `compile_commands.json`, so make sure you run the\n    `./tools/scripts/build_compilation_db.sh` script.\n\n[Compilation Database]: https://clang.llvm.org/docs/JSONCompilationDatabase.html\n[rtags]: https://github.com/Andersbakken/rtags\n[clangd]: https://clang.llvm.org/extra/clangd.html\n[clang-format]: https://clang.llvm.org/docs/ClangFormat.html\n[CLion]: https://www.jetbrains.com/clion/\n[vscode-clangd]: https://marketplace.visualstudio.com/items?itemName=llvm-vs-code-extensions.vscode-clangd\n\nHere are some sample config setups:\n\n- Vim\n  - [rtags (vim-rtags)](https://github.com/jez/dotfiles/blob/dafe23c95fd908719bf477f189335bd1451bd8a7/vim/plug-settings.vim#L649-L676)\n  - [clangd + clang-format (ALE)](https://github.com/jez/dotfiles/blob/dafe23c95fd908719bf477f189335bd1451bd8a7/vim/plug-settings.vim#L288-L303)\n  - [clangd + clang-format (coc.nvim)](https://github.com/elliottt/vim-config/blob/35f328765528f6b322fb7d5a03fb3edd81067805/coc-settings.json#L3-L15)\n\n### Editor setup for test/testdata\n\nSorbet developers will want to set up their editor to run Sorbet slightly\ndifferently from normal Sorbet users. The two main goals of a Sorbet developer's\nsetup:\n\n1.  Use `bazel-bin/main/sorbet` to see Sorbet changes in your editor as you\n    rebuild\n1.  Get `test/testdata/` and other files to typecheck\n\nSince there are so few Sorbet developers, every setup is custom.\n\n#### @jez setup\n\n1.  Create a `sorbet` symlink in `$PATH` somewhere, and have it point at\n    `/path/to/sorbet/bazel-bin/main/sorbet`.\n\n1.  Configure LSP to invoke the Sorbet LSP server like this:\n\n    - If there is a `Gemfile` in the current folder, use `srb tc`. Otherwise,\n      use `sorbet` (from the `$PATH` setup, see above).\n    - If there is a `sorbet/config` file in the repo, invoke with no extra args.\n      Otherwise, invoke with these args:\n\n      ```\n      -e 0 /Users/jez/.local/share/sorbet/.empty --debug-log-file=/tmp/sorbet-nvim.log\n      ```\n\n      This should mean that opening any Ruby file in `test/testdata/` should\n      have Sorbet start automatically and only typecheck that file in isolation,\n      instead of attempting to typecheck all of `test/testdata/` as if it were\n      one project.\n\n#### others\n\n_Feel free to add yours if you think it would be useful!_\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorbet%2Fsorbet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsorbet%2Fsorbet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsorbet%2Fsorbet/lists"}