Rustコンパイラ組込みのlintについて調べる

はじめに

Rustコンパイラであるrustcには組込みのlintがあります。 Rustのlintツールでは、clippyが有名ですが、rustc組込みの方もソースコードの改善に役立ちます。

7月の技術書同人誌博覧会のネタ探しの一環だったのですが、思ったより世の中の役に立ちそうだったので、ドラフトを公開します。 原稿落とさなければ、「組込み / ベアメタルRustテクニック集」的な薄い本を出す予定です。

ちなみにここに書いている内容は、全てThe rustc bookに書いてあります。

lint

lintソースコードコンパイラより厳密なルールに則り、検査するためのツールです。 Rustコンパイラには、様々なlintルールが組み込まれています。 ソースコードコンパイルする時、自動的にlintによる検査が行われます。

プロジェクトの運用ルールに合わせて、適切なlintルールを設定することで、ソースコードの品質をより向上できるでしょう。

lintレベル

rustcのlintレベルは、4つに分類されます。

  1. allow (許可)
  2. warn (警告)
  3. deny (拒絶)
  4. forbid (禁止)

各lintルールには、デフォルトのlintレベルがあり、コンパイルオプションかアトリビュートで上書きできるようになっています。 まず、lintレベルについて説明します。

allow (許可)

lintルールを適用しません。 例えば、次のコードをコンパイルしても、警告は発生しません。

pub fn foo() {}
$ rustc lib.rs --crate-type=lib

しかし、このコードはmissing_docsルールを違反しています。 lintレベルを上書きしてコンパイルすると、コンパイルエラーになったり、警告が出力されるようになります。

warn (警告)

lintルール違反があった場合、警告を表示します。

fn main() {
    let x = 5;
}

このコードは次の警告が報告されます。

warning: unused variable: `x`
 --> src/main.rs:2:9
  |
2 |     let x = 5;
  |         ^ help: consider prefixing with an underscore: `_x`
  |
  = note: #[warn(unused_variables)] on by default

deny (拒絶)

lintルール違反があった場合、コンパイルエラーになります。

fn main() {
    100u8 << 10;
}

このコードは、exceeding_bitshiftsルールに違反しており、コンパイルエラーになります。

error: attempt to shift left with overflow
 --> src/main.rs:2:5
  |
2 |     100u8 << 10;
  |     ^^^^^^^^^^^
  |
  = note: #[deny(exceeding_bitshifts)] on by default

forbid (禁止)

lintルール違反があった場合、コンパイルエラーになります。 forbidは、denyより強いレベルで、上書きができません。

下のコードは、アトリビュートmissing_docsルールをallowに上書きしています。

#![allow(missing_docs)]
pub fn foo() {}

missing_dogsルールを、denyレベルに設定してコンパイルすると、このコードはコンパイルできます。

$ rustc lib.rs --crate-type=lib -D missing-docs

一方、forbidレベルに設定してコンパイルすると、コンパイルエラーになります。

$ rustc lib.rs --crate-type=lib -F missing-docs
error[E0453]: allow(missing_docs) overruled by outer forbid(missing_docs)
 --> lib.rs:1:10
  |
1 | #![allow(missing_docs)]
  |          ^^^^^^^^^^^^ overruled by previous forbid
  |
  = note: `forbid` lint level was set on command line

lintレベルの設定方法

コンパイラフラグで設定

コンパイルオプションで、-A, -W, -D, -Fのいずれかを指定して、lintレベルを設定できます。

$ rustc lib.rs --crate-type=lib -W missing-docs

もちろん、複数のフラグを同時に設定することも可能です。

$ rustc lib.rs --crate-type=lib -D missing-docs -A unused-variables

Cargoの設定ファイル内で、lintレベルを設定することも可能です。

$ cat .cargo/config
[build]
rustflags = ["-D", "unsafe-code"]

アトリビュートで設定

ソースコード内のアトリビュートで、allow, warn, deny, forbidのいずれかを指定して、lintレベルを設定できます。

$ cat lib.rs
#![warn(missing_docs)]

pub fn foo() {}

1つのアトリビュートに、複数のlintルールを指定できます。

#![warn(missing_docs, unused_variables)]

fn main() {
pub fn foo() {}
}

複数のアトリビュートを組み合わせて使うこともできます。

#![warn(missing_docs)]
#![deny(unused_variables)]

pub fn foo() {}

lintルール

次のコマンドでlintルールと、デフォルトレベルの一覧が取得できます。

$ rustc -W help

デフォルトレベルごとに、サンプルコード付きでlintルールが説明されています。

知られざるlintルールたち

ちらっと目について、へー、と思ったルールを独断と偏見で紹介します。

ちょっとマニアック

自分が知らんかった枠