smith check、fmt、lint 和 lsp
smith fmt、smith lint 和 smith lsp 是 clang-format、clang-tidy 和 clangd,它們從與編譯器相同的 LLVM 連結到 smith,因此每台機器的格式化和檢查結果都一致。
針對專案:無參數
這些動詞在每個專案中的意義相同,並讀取專案的 build.c 所宣告的內容;專案不需要為它們撰寫任務。
smith fmt會改寫 git 看到的每個 C、C++、Objective-C 和.smith檔案(已追蹤、未追蹤但未被忽略的檔案;.c .h .m .mm .cpp .cc .cxx .hpp .hh .smith .metal),使其符合.clang-format(.metal檔案會被格式化為 C++,這是 clang-format 對其未知副檔名所採用的語言)。smith fmt --dry-run(或-n,搭配--Werror或-i)和smith lint --quiet也是專案形式:僅使用選項,沒有檔案名稱,因此它們永遠不會等待標準輸入;任何其他選項或檔案名稱,都會使它們變成工具(smith fmt --assume-filename=x.c會讀取標準輸入)。.clang-format-ignore中列出的檔案(clang-format 本身會讀取它:例如一個自動產生的parser.c或測試 fixture)會被忽略。如果在 git 儲存庫之外,它會要求指定檔案名稱(smith fmt -i <files>)。smith lint會建置編譯器所讀取的內容(產生的標頭檔),然後使用.clang-tidy對專案自己的原始碼(來自建置的編譯目標,例如smith_object,以及smith_compile_command用於建置從未編譯的原始碼)進行檢查,這些檢查是並行進行的。不包括.smith/、build/的輸出或套件。每個.smith原始碼都會像smith lint x.smith -- <flags>一樣被檢查:專案自己的原始碼使用其編譯命令,而 git 看到的其他每個.smith檔案則使用最近的編譯命令,但.clang-format-ignore中命名的檔案除外(這是唯一一個非普通原始碼的檔案清單:測試輸入、故意錯誤的 fixture、自動產生和複製的檔案;smith fmt也會讀取它,因此不會有第二個清單)。任何發現都會使 lint 失敗;其最後一行會顯示linted N files(smith check:lint ok: N files),而一個包含.smith原始碼且未檢查任何檔案的專案則會失敗。資料夾中的.clang-tidy(例如使用InheritParentConfig: true和其他檢查)會套用至該資料夾。smith lint是增量式的:clang-tidy 對某個檔案的結果會快取在~/.cache/smith/lint下,當檔案、其包含的內容(對於.smith檔案,還包括其匯入的模組所產生的標頭檔)、其.clang-tidy和 smith 本身未變更時,會重複使用該快取,因此最後一行會顯示smith lint: linted 180 files (3 run, 177 cached, 1.2 s),未變更的樹狀結構不會執行 clang-tidy。SMITH_LINT_CACHE=0會關閉快取,SMITH_LINT_CACHE=why則會說明檔案為何被執行,smith cache clean lint會清空快取。clang-tidy 失敗的檔案會在最末幾行顯示(smith lint: c.c: exited with status 1,或was killed by signal 9,這不會列印其自身的輸出),並在其自身輸出的結尾處顯示。smith lint --changed[=<ref>]僅檢查自與<ref>的合併基礎以來已變更的檔案(預設:分支的上游,否則為origin/HEAD的分支或origin/main,絕不會是本機的main;如果這些都不存在,則為使用--changed=<ref>命名時的錯誤;列印的行會命名基礎;編輯和未追蹤的檔案也算在內),以及包含或匯入它們的檔案;已變更的.clang-tidy、建置檔案、shim/或patches/會選擇所有檔案,如果沒有任何檔案受影響,則會顯示成功訊息。它也只會建置這些檔案所等待的內容。普通的smith lint會檢查所有內容(CI)。如果某個檔案所等待的套件無法從網路取得(例如雲端沙箱中的 tree-sitter 解析器),則會按名稱跳過該檔案(smith lint: skipped runtime/tree_sitter_data.c: the tree-sitter parser it embeds cannot be fetched (<url>)),其餘部分仍會被檢查;CI(CI或SMITH_PRIORITY=ci)則會失敗。在尚未建置的專案(沒有compile_commands.json)中執行smith lint FILE會停止並提示執行一次smith build smith_needs,而不是將缺少的產生標頭檔報告為錯誤。smith docs會檢查knowledge/和技能(smith-docs)。smith check會依序執行 fmt(clang-format 的乾執行)、docs、lint、test 和 prove,並且不會在失敗時停止:每個部分會列印一行,smith check: <part> ok: <detail> (<n> s wall, <n> s cpu)或FAILED (status N),並附上失敗內容的有限摘錄和完整日誌的路徑(.smith/log/<part>.log;通過的部分的輸出也會存放在那裡;smith check -v會即時列印所有內容),然後是smith check: promises: N(smith 信任的內容,來自於必要時進行的預設建置),最後一行是smith check: ok或smith check: failed: <parts>;任何部分失敗時退出狀態為 1。prove 行會顯示覆蓋率,例如smith check: prove ok (4 of 23 functions): ...(專案中具有自身合約的函式;0 of N functions表示沒有需要證明的內容,這是成功;低覆蓋率不會導致失敗)。每個部分的行會以其牆面時間和 CPU 時間結尾((91.2 s wall, 480.1 s cpu):CPU 是使用者加系統時間,包括該部分及其等待的進程)。它不接受任何參數,除了-D選項和-v。在非 macOS 的主機上,它會使用--skip-macos執行測試,並說明(the macOS-only tests do not run on this host)。smith test會以表格形式顯示建置的任務(.smith/task/<name>步驟,如 smith 自己的test-<name>),按最慢者優先排序:牆面、CPU 和系統時間(秒),以及每個任務啟動的 smith 進程數(SMITH_START_LOG,smith 在main開始時會附加到該日誌;清除環境的任務不會被計數);顯示最慢的八個任務,若使用smith check -v或SMITH_ACCOUNT=all則顯示所有任務,若使用SMITH_ACCOUNT=no則不顯示。這些任務透過smith --smith-account <file> <name> <command...>執行,該命令會寫入該行。- 新語法的
.smith檔案不屬於 clang-format(#1890, #1886, #2040):smith fmt會將.smith檔案傳送給 smith 自己的格式化工具(front::format),除非該檔案使用目前的語法(某行以僅有目前語法或 C 才有的形式開頭,目前的解析器可以無錯誤地讀取它,或者其新語法中的第一個錯誤具有修復,例如//註解),或者有解析錯誤且沒有任何行僅包含新語法(fn、let、pub、#):一種樣式,沒有.clang-format(任何一致的縮排都為 4 個空格,每行一個陳述式,保留#註解)。具有解析錯誤的新語法檔案會以path:line:column: message格式報告,原封不動地保留位元組(即使在--dry-run模式下也是如此),並使smith fmt以退出狀態 1 結束:它絕不會被傳送給 clang-format,因為 clang-format 會破壞它。它是冪等的,遵循.clang-format-ignore,並且--dry-run/-n會列出將會變更的檔案名稱(path: would be formatted)而不寫入。目前語法的.smith檔案(以及每個.c)在切換(#1631)移除此後備方案之前,仍由 clang-format 負責。由tests/fmt_smith_test.smith測試。 smith fmt FILE會拒絕副檔名不屬於上述類別的檔案(例如.sh、.md),並顯示錯誤,指出該檔案名稱且不寫入任何內容,因為 clang-format 會將其讀取為 C 並破壞它;--assume-filename=x.c可用來說明其類型。- 提交之前:先執行
smith fmt,然後執行smith check。 - 你自己的任務(自訂檢查、產生器)是
build.c中的smith_task,並透過smith build <task>執行;任何任務或別名都不能命名為fmt、lint、docs、prove、check、run或test。smith build check和其他動詞名稱會被拒絕:請使用動詞。
針對檔案:使用參數
smith lint DIR(參數中的資料夾)會檢查該資料夾下 git 看到且 .clang-format-ignore 未命名的 C、C++、Objective-C 和 .smith 原始碼,類似於專案檢查(不檢查標頭檔,標頭檔是透過其原始碼讀取的);smith lint front -- <flags> 和 smith lint front/ main.c 可以與檔案混合。如果資料夾中沒有此類原始碼,則會報錯。
clang-tidy 的分析器(clang-analyzer-*)會將 <smith/contract.h> 中的 smith_requires、smith_ensures、smith_invariant 和 smith_assert 讀取為假設(target::static_analyzer),因此受合約約束的索引不會被 clang-analyzer-security.ArrayBound 報告。
smith 的預設 .clang-tidy 會關閉兩個造成雜訊的分析器檢查(在遊戲、學習者和 smith 上:616 個發現中有 546 個):clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling(每個 memcpy)和 clang-analyzer-optin.performance.Padding。若要重新啟用其中一個,請在專案自己的 .clang-tidy 中刪除其 -clang-analyzer-... 行(smith lint --dump-config > .clang-tidy 會寫入有效的檔案)。
smith lint --fix FILE.smith...(不加 --)會套用 smith 自己的前端在其解析錯誤中所攜帶的修復(舊形式或 C 風格形式及其 smith 拼寫:一個修復的編輯會一起套用,否則完全不套用,重疊的修復會被跳過,並且檔案會重新解析,直到沒有任何修復可套用,最多 16 次)並列印 path: N fixes applied;第二次執行會列印 0 fixes applied。它只讀取 .smith 檔案,並且需要完整的 smith。搭配 -- <flags> 時,它是 clang-tidy 的 --fix,如上所述。目前還沒有解析錯誤攜帶修復:舊形式會逐一獲得它們的修復(#1428)。
smith lint FILE 會在該資料夾存在時(前端模組透過該資料夾相互匯入)新增 --extra-arg=-I 指向 build/<os>/front-project/.smith/include,並且在非 macOS 的主機上會列印一行:CI 在 Mac 上進行檢查,其中 u64 是 unsigned long long,usize 是 unsigned long,因此 (U64)(i + 1) 搭配 usize 只會在該處產生 bugprone-misplaced-widening-cast 發現;請透過撰寫 (U64)i + 1 來修復此類轉換。
使用參數時,它們就是工具:smith fmt main.c 會就地改寫檔案(clang-format 會列印它;smith 在指定檔案名稱且沒有選項選擇其他模式時會新增 -i:-n、--dry-run、--lines、--assume-filename 保留 clang-format 的意義,而 smith fmt --assume-filename=x.c < x.c 則會列印結果),smith fmt --dry-run --Werror main.c、smith fmt --dry-run --Werror main.c、smith lint --quiet main.c -- <flags>、smith lsp --check=main.c。專案的 .clang-format 和 .clang-tidy 會套用。stage0 smith 會執行 Homebrew 的工具,或 $SMITH_CLANG_FORMAT、$SMITH_CLANG_TIDY、$SMITH_CLANGD。
smith 的預設值,適用於專案沒有對應檔案的情況(問題 #43)。如果專案沒有 .clang-format(或 _clang-format)和 .clang-tidy(從每個檔案的資料夾向上查找,就像 clang-format 和 clang-tidy 所做的那樣),則會使用 smith 自己內嵌的預設值進行格式化和檢查(這些是 smith new 今天寫入的檔案);無需撰寫任何內容。專案中的檔案會完全取代 smith 的預設值(不會合併),而明確的 --style(或 --fallback-style)、--config 或 --config-file 則會優先。對於標準輸入,--assume-filename 會指定從哪裡查找。在同一個命令中混合使用有專案檔案和沒有專案檔案的檔案是可行的(smith 會將第二種類型作為另一個進程執行)。smith fmt --dump-config > .clang-format 和 smith lint --dump-config > .clang-tidy 會列印有效的配置(專案的,否則為 smith 的),因此專案可以透過傾印預設值並編輯所需內容來採用這些預設值;預設值很少且有意義地更改,因此希望固定使用某個預設值的專案可以傾印它們。smith 會將其副本寫入 $XDG_DATA_HOME/smith/defaults/(否則為 ~/.local/share/...)。smith cc 永遠不會受影響:它保持純透通。
.smith 檔案
smith fmt x.smith(以及 -i、--dry-run、使用 --assume-filename=x.smith 的標準輸入)會將 .smith 檔案格式化為 C,保留其 :: 路徑、module a::b; 和 import a::b as x; 行以及 import <sys/wait.h>; 行的完整性;不帶參數的 smith fmt 也會格式化專案的 .smith 檔案(git 會看到它們)。函式內部的 import 和 module 是名稱,就像在 C 中一樣。需要一個其 clang-format 具有 .smith 修補程式的 smith(stage0 執行 Homebrew 的 clang-format,它將 .smith 讀取為 C++,因此會誤讀為 C++ 關鍵字的 C 名稱,例如 throw)。clangd 以相同方式格式化 .smith 檔案。
smith lint x.smith -- <flags>(clang-tidy,使用專案的 .clang-tidy)會以 smith cc 讀取 .smith 檔案的方式對其進行檢查(-x c,除非你指定了標準,否則使用 -std=gnu2y;編譯資料庫自身的命令會按原樣使用),並以原始碼的撰寫方式報告:發現、說明和修復建議會對應到 .smith 文字的行和欄,並使用原始碼中撰寫的名稱(模組中的 area 和 Box,以及匯入時的 box::area,絕不會是 geometry_box_area)。readability-identifier-naming 會判斷模組檔案作用域名稱的撰寫名稱,因此樣板的規則(snake_case 函式和變數,PascalCase 類型)會套用於 area 和 Box,而 --fix 會改寫原始碼中的記號;有大小限制的類型(U32、I32)、降級產生的原型副本以及 smith build 寫入的標頭檔永遠不會被報告。關於某個匯出項目的發現會在該模組的檔案中報告,而不是在每個匯入者中報告,並且在那裡進行的修復不會重新命名匯入者。其他檢查會在其訊息中顯示撰寫的名稱,但比較名稱本身的檢查(例如長度或保留名稱檢查)仍然會讀取降級後的名稱。需要一個其 clang 具有記號鉤子的 smith(smith cc --smith-which x.smith 會顯示 token-hook yes);stage0 執行 Homebrew 的 clang-tidy,它無法讀取 .smith。
也屬於這個系列:smith-check-lints。