Swift Digest
SE-0196 | Swift Evolution

Compiler Diagnostic Directives

Proposal
SE-0196
Authors
Harlan Haskins
Review Manager
Ted Kremenek
Status
Implemented (Swift 4.2)

01 何が問題だったのか

開発中のコードベースには、未完成の部分や後で見直すべき箇所を示すために TODOFIXME といったコメントを残すことがよくあります。多くのエディタはこれらのコメントをコードナビゲータに表示してくれますが、コマンドラインでのビルドや継続的インテグレーション (CI) では表示されないため、チームや自分自身に確実に気付かせる手段がありませんでした。

また、二つのビルド構成が排他的な関係にあり「この組み合わせでビルドしてはいけない」ことをコンパイル時に検出したい場合もあります。Swift にはそのためのしくみがなく、条件付きコンパイルで不整合を検出して確実にビルドを失敗させる手段が不足していました。

これらは性質の異なる課題ですが、いずれも「コンパイル時に開発者へ確実にメッセージを伝える」ための仕組みが欠けているという共通点があります。

02 どのように解決されるのか

コンパイラディレクティブとして #warning(_:)#error(_:) を導入します。いずれも引数に静的な文字列リテラルを取り、パース時点でその内容を警告またはエラーとして出力します。

#warning の使い方

#warning はコンパイルを止めずに警告を出します。コードテンプレートで「ここを埋めてください」と指示したいときや、あとで見直したい箇所に印を付けたいときに便利です。

enum APICredentials {
  #warning("fill in your API key below")
  static let key = ""
  #warning("fill in your API secret below")
  static let secret = ""
}
func configPath() -> String {
  #warning("this should be made more safe")
  return Bundle.main.path(forResource: "Config", ofType: "plist")!
}

#error の使い方

#error はコンパイルをエラーで失敗させます。ビルド構成の組み合わせが不正であることをコンパイル時に検出したい場合などに使います。

#if MYLIB_VERSION < 3 && os(macOS)
#error("MyLib versions < 3 are not supported on macOS")
#endif

#if との組み合わせ

#warning#error#if の通らない分岐の中にある場合は、診断は出力されません。これにより、特定のビルド構成のときだけ警告やエラーを出すことができます。

#if false
#warning("this will not trigger a warning")
#error("this will not trigger an error")
#endif

#if true
#warning("this will trigger a warning")
#error("this will trigger an error")
#endif

動作の詳細

  • 引数は静的な文字列リテラル(static-string-literal)に限られます。実行時に組み立てた文字列は渡せません。
  • 診断の位置は、文字列リテラルの先頭を指します。
  • #error が出現してもコンパイラはディレクティブ自体を無視してそのまま処理を続けます(そのためエラーは出るものの、以降の解析は行われます)。

今後の展望

本提案では #warning#error は行ディレクティブとしてのみ提供され、式の中で使うことはできません。レビューの過程では式として使える #warning(値を包みつつ警告を出す用途)や、警告扱いにしない純粋な情報メッセージ用の #message / #info なども議論されましたが、本提案のスコープ外とされ、将来的な拡張の余地として残されています。