Skip to main content

執行 Lint 檢查 (Running Lints)

Move 編譯器隨附一組 lint 檢查——會在編譯時標記原始碼中可疑模式的靜態檢查。測試會驗證原始碼是否如預期運作;lint 檢查則會找出能夠編譯、甚至可能通過測試,但資深 Move 開發者不會這樣撰寫的原始碼:破壞可組合性的轉移、結果永遠與表面意義不同的比較,或永遠無法呼叫的 entry 函式。定期執行 lint 檢查,並讓套件不含警告,是維持原始碼品質的低成本方式。

執行 Lint 檢查 (Running Lints)​

sui move lint 指令會編譯套件,並執行完整的 linter 集合:

sui move lint

若也要檢查 tests 目錄中的原始碼,請加入 --test 旗標:

sui move lint --test

相同檢查也能透過其他指令的 --lint 旗標使用——例如,sui move test --lint 會一次執行測試與完整 lint 集合。

假設某個模組有一個函式,會將新建立的物件轉移給交易傳送者:

module book::mint;

public struct Item has key, store { id: UID }

public fun mint(ctx: &mut TxContext) {
let item = Item { id: object::new(ctx) };
transfer::transfer(item, ctx.sender());
}

執行 linter 會印出警告,其中包含說明及指向確切運算式的指標:

warning[Lint W99001]: non-composable transfer to sender
┌─ ./sources/mint.move:7:5
│
5 │ public fun mint(ctx: &mut TxContext) {
│ ---- 從函式回傳物件可讓呼叫端使用該物件,並透過可程式化交易啟用可組合性。
6 │ let item = Item { id: object::new(ctx) };
7 │ transfer::transfer(item, ctx.sender());
│ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
│ │ │
│ │ 交易傳送者地址來自此處
│ 將物件轉移至交易傳送者地址
│
= 可將 '#[allow(lint(self_transfer))]' 套用至 'module' 或模組成員('const'、'fun' 或 'struct')以抑制此警告

此特定 lint 檢查建議的修正方式,是從函式回傳 Item,而非轉移它,並讓呼叫端決定如何處理該物件。

預設與額外 Lint 檢查 (Default and Extra Lints)​

Lint 檢查分為兩個層級。預設層級包含最重要的 Sui 專用檢查,且會在每次編譯時執行——單純的 sui move build 或 sui move test 也會回報這些警告。額外層級會加入另外兩項 Sui 檢查及一組原始碼風格 lint 檢查;僅在明確要求執行 lint 檢查時執行,例如使用 sui move lint 或 --lint 旗標。

抑制 Lint 檢查 (Suppressing Lints)​

Lint 檢查是啟發式檢查,有時被標記的原始碼是刻意如此。可使用 #[allow(lint(<name>))] 屬性抑制 lint 檢查;請將它套用至模組或模組成員,並使用警告中印出的 lint 名稱:

public struct Account has key { id: UID }

/// 帳戶物件,刻意為傳送者建立並由其擁有。
#[allow(lint(self_transfer))]
public fun new_account(ctx: &mut TxContext) {
transfer::transfer(
Account { id: object::new(ctx) },
ctx.sender(),
);
}

單一屬性可抑制多個 lint 檢查:#[allow(lint(share_owned, self_transfer))]。請將抑制視為其他例外情況——保持範圍精確(優先套用至函式,而非整個模組),並在註解或文件註解中說明原因。

CI 中的 Lint 檢查 (Lints in CI)​

若要強制維持不含警告的原始碼庫,請加入 --warnings-are-errors 旗標——此時只要出現任何警告(包括 lint 檢查),指令便會以非零結束代碼失敗:

sui move lint --test --warnings-are-errors

對於以程式方式取用輸出的工具,--json-errors 會將診斷資訊切換為 JSON 格式。

Lint 檢查參考 (Lint Reference)​

Linter 會將檢查分成兩組:每次編譯都會執行的 預設 lint 檢查,以及僅在使用 --lint 旗標時執行的 額外 lint 檢查。

預設 Lint 檢查 (Default Lints)​

這些檢查會在每次編譯時執行:

Lint 檢查代碼標記項目
share_ownedW99000分享先前可能已被擁有的物件;請在建立物件的交易中分享它
self_transferW99001將新物件轉移給傳送者,而非回傳它;會損害可組合性
custom_state_changeW99002對具有 store 的型別套用自訂轉移/分享/凍結策略;public_* 儲存函式 可繞過它
coin_fieldW99003型別為 Coin<T> 的 struct 欄位;Balance<T> 成本更低,通常也是正確的選擇
freeze_wrappedW99004凍結包裝其他物件的物件
collection_equalityW99005使用 == 比較動態集合;只會比較 id 與 size,絕不比較內容
public_randomW99006接受 Random 的 public 函式;會使隨機性暴露於組合攻擊
missing_keyW99007具有 id: UID 欄位但沒有 key ability 的 struct
public_entryW99010public 函式上不必要的 entry 修飾詞
uncallable_functionW99011永遠無法在交易中呼叫的函式,例如接受 &mut Clock 的 entry 函式

額外 Lint 檢查 (Extra Lints)​

由 sui move lint 或 --lint 旗標啟用:

Lint 檢查代碼標記項目
freezing_capabilityW99008凍結看似能力的型別
prefer_mut_tx_contextW99009接受 &TxContext 的 public 函式;請優先使用 &mut TxContext,以讓函式簽名能因應未來變更

額外層級也包含原始碼風格 lint 檢查(代碼為 W04xxx):constant_naming、while_true、unnecessary_math、unneeded_return、abort_without_constant、loop_without_exit、unnecessary_conditional、self_assignment、redundant_ref_deref、unnecessary_unit、always_equal_operands 與 combinable_comparisons。每項檢查都會標記一個小型的可讀性或正確性問題,並建議較簡單的等效寫法。

總結 (Summary)​

指令說明
sui move lint編譯套件並執行完整 lint 集合
sui move lint --test也對 tests 目錄中的原始碼執行 lint 檢查
sui move lint --warnings-are-errors任一警告即失敗——適用於 CI
sui move build / sui move test執行預設 lint 層級
sui move test --lint使用完整 lint 集合執行測試
--no-lint完全停用 linter

延伸閱讀 (Further Reading)​