この記事の要点
- Xcode や SourceKit-LSP のコード補完・セマンティックハイライト・リファクタリングといったエディタ機能を支える sourcekitd の堅牢性を高めるため、新しいテストツール sourcekitd stress tester が導入されました。
- このツールは、Swift のソースコードに対して大量の sourcekitd リクエストを自動生成して送り込み、クラッシュ・アサーション失敗・ハングといった不具合を洗い出します。これまでに 91 件の再現可能な問題を発見しています。
- sourcekitd stress tester は macOS 向けの開発版(trunk development snapshot)ツールチェインに同梱されており、Swift 開発者が自分のプロジェクトに対して実行し、見つかった不具合を報告することで、Swift の編集体験の改善に貢献できます。
何が発表されたのか
sourcekitd は、Xcode や SourceKit-LSP などのクライアントに対して、Swift ソースファイルのコード補完・セマンティックハイライト・リファクタリングといった機能のデータを提供するサービスです。クライアントとはリクエスト・レスポンス形式で通信します。
その堅牢性を高めるために導入されたのが sourcekitd stress tester です。macOS 向けの trunk development snapshot のツールチェインには、次の 2 つの実行ファイルが含まれます。
- sk-stress-test: stress tester 本体。単一の Swift ソースファイルとそのコンパイラ引数を受け取り、ドキュメントを開く・編集する・問い合わせる・閉じるという一連の sourcekitd リクエストを生成して順番に送ります。クラッシュ・ハング・基本的なチェックに失敗するレスポンスを引き起こした最初のリクエストで失敗を報告し、再現に必要な情報(トリガーとなったリクエストと、その直前のドキュメントの状態)を出力します。
- sk-swiftc-wrapper:
swiftcのドロップイン代替として振る舞うヘルパーです。渡されたコンパイラ引数をそのままswiftcに渡してコンパイルし、成功した場合はコンパイル対象の各 Swift ソースファイルに対して stress tester を実行します。これにより、プロジェクト全体に対して stress tester を回すことが容易になります。
stress tester がどのリクエスト列を送るかは、--rewrite-mode オプションで選べる 4 つの戦略で決まります。いずれもソースファイルの構文情報をもとにリクエストを生成し、入力ファイルをさまざまな形で書き換えながら(あるいはそのまま使いながら)リクエストを送る点が共通しています。
- none(デフォルト): 入力ファイルをそのまま開き、書き換えずにリクエストを送ります。有効なソースコードを閲覧・ナビゲートするだけで起こる不具合を見つけるため、優先度の高い問題を捕捉しやすい戦略です。
- basic: 空のドキュメントから始め、トークンを上から順に挿入しながら、その前後でセマンティックリクエストを送ります。コード補完などは構文的に不完全な状態のソースに対して呼ばれることが多いため、その状況を再現します。
- concurrent: basic と似ていますが、ファイル内の各トップレベル宣言を並行に書いていくかのように、宣言をまたいでトークンを順番に挿入します。後方の宣言が一時的に前方の宣言の内部にネストされ、無効なコンテキストが生まれます。
- insideOut: トークンを、構文木上で最も深くネストされたものから順に挿入します。SwiftSyntax や、sourcekitd が使うインクリメンタルパースのロジックの不具合を見つけるのに有用でした。
何に使えるのか
stress tester は Swift の CI とプルリクエストのテストに組み込まれています。
- CI での回帰テスト: Swift ソース互換性テストスイートに含まれる 78 個のオープンソースプロジェクトに対して stress tester を実行しています。実行時間が長いため、全体は週に 1 回、より小さなサブセットは sourcekitd やコンパイラに変更があるたびに継続的に回しています。これにより、これまでに 91 件の問題が見つかり、執筆時点で 72 件が修正されました。
- プルリクエストでのテスト: コントリビュータは、PR のコメントに
@swift-ci please stress testと書くことで、ソース互換性テストスイートのサブセットに対して stress tester を実行できます。
sourcekitd はコンパイラと多くのコードを共有し、コンパイラが通常扱うよりもはるかに広い範囲の無効な Swift ソースコードを扱います。そのため、ここで見つかった不具合の修正は sourcekitd の編集体験だけでなく、コンパイラ本体の改善にもつながっています。
自分のプロジェクトに対して実行することもできます。
- Xcode プロジェクト: trunk development snapshot のツールチェインをインストールして選択し、ユーザー定義のビルド設定
SWIFT_EXECに$(TOOLCHAIN_DIR)/usr/bin/sk-swiftc-wrapperを指定してビルドします。 - Swift Package Manager プロジェクト:
SWIFT_EXEC環境変数にsk-swiftc-wrapperのパスを設定してswift buildを実行します。
stress testing は重い処理のため、ビルドは通常よりかなり長くなります。不具合が見つかった場合は、ビルドログに出力される再現情報を添えて bugs.swift.org に報告し、FoundByStressTester ラベルを付けることが推奨されています。
導入・今後の位置づけ
sourcekitd stress tester は比較的シンプルなツールですが、ソース互換性テストスイートに対する実行結果と CI への組み込みによって、Xcode と SourceKit-LSP の編集体験の信頼性に大きく寄与すると見込まれていました。すべてのプロジェクトのすべてのファイルのすべてのトークンに対してコード補完やローカルリファクタリングが確実に動作することを確認できるため、sourcekitd やコンパイラへの変更が既存機能を退行させていないという確信を得られます。
執筆時点では実行ファイルは macOS のツールチェインでのみ提供されていましたが、Linux 対応に本質的な障壁はなく、未実装なだけだとされていました。新しいリクエスト生成戦略の追加など、コントリビューションも歓迎されています。