clang-format pada chapter sebelumnya hanya menata karakter — ia berhenti sebelum menilai apa arti baris tersebut. clang-tidy melangkah lebih jauh: alat ini membaca kode sebagai pohon sintaks (AST) hasil frontend Clang, lalu menjalankan ratusan check bernama yang masing-masing mengenali satu pola kode dan menawarkan perbaikannya. Hasilnya bukan sekadar “ada masalah di baris ini”, melainkan “masalah apa, dari kategori apa, dan biasanya diperbaiki seperti apa” — laporan yang bisa langsung dieksekusi. clang-tidy tidak terinstal di sandbox penulis, sehingga laporan yang Anda lihat di chapter ini adalah ilustrasi formatnya; tetapi kode yang menjadi targetnya kita kompilasi dan jalankan sungguhan, karena seleranya persis itu: kode yang berjalan baik di mata compiler, tetapi menyimpan beberapa kebiasaan yang layak diperbaiki.
Empat Kategori Check yang Paling Sering Dipakai #
Check clang-tidy dikelompokkan dengan awalan nama, dan hampir seluruh kerja sehari-hari berada di empat kategori ini:
| Kategori | Fokus | Contoh check |
|---|---|---|
bugprone-* | pola yang hampir pasti menyimpan bug | bugprone-undefined-memory-manipulation, bugprone-narrowing-conversions |
performance-* | pemborosan yang tak disadari penulisnya | performance-unnecessary-value-param, performance-for-range-copy |
modernize-* | gaya lama yang punya pengganti modern | modernize-use-nullptr, modernize-make-unique, modernize-loop-convert |
readability-* | kejelasan tanpa mengubah perilaku | readability-identifier-naming, readability-const-return-type |
Di luar itu, clang-tidy juga menjalankan diagnostic compiler Clang pada baris yang dipindai — jadi sebagian warning yang sudah kita pelajari pada chapter Compiler Warnings muncul kembali di sini. Konsep lain yang membuat clang-tidy praktis adalah fix otomatis: banyak check menyertakan perbaikan siap terapkan, dan flag -fix membuat clang-tidy menulisnya langsung ke berkas — perbaikan pada kategori modernize biasanya aman untuk diterapkan massal.
Studi Kasus: Kode yang Berjalan Baik, Menurut clang-tidy Belum #
Target kita berjalan mulus, lolos -Wall -Wextra tanpa satu pun peringatan, dan menghasilkan output yang benar:
// kandidat-tidy.cpp — berjalan dengan benar, tetapi clang-tidy punya
// beberapa saran perbaikan untuk kode ini (modernize-* dan performance-*).
#include <iostream>
#include <memory>
#include <string>
#include <vector>
struct Profil {
std::string nama;
int level;
};
void tampilkan(Profil profil) {
std::cout << profil.nama << " level " << profil.level << "\n";
}
int main() {
std::vector<int> nilai = {4, 8, 15};
int total = 0;
for (std::size_t i = 0; i < nilai.size(); ++i) {
total += nilai[i];
}
std::unique_ptr<Profil> pemilik(new Profil{"Budi", 3});
int* petunjuk = 0;
if (petunjuk == nullptr) {
std::cout << "petunjuk belum menunjuk apa-apa\n";
}
tampilkan(*pemilik);
std::cout << "total: " << total << "\n";
return 0;
}Output yang Dihasilkan:
petunjuk belum menunjuk apa-apa
Budi level 3
total: 27Sekarang jalankan clang-tidy dengan perintah berikut — perhatikan tanda -- yang memisahkan argumen clang-tidy dari flag compiler:
clang-tidy -checks='-*,performance-*,modernize-*' kandidat-tidy.cpp -- -std=c++23Format laporannya (ilustrasi — clang-tidy tidak terinstal di sandbox; teks pesan bisa sedikit berbeda antar versi):
kandidat-tidy.cpp:14:23: warning: the parameter 'profil' is copied for each invocation but only used as a const reference; consider making it a const reference [performance-unnecessary-value-param]
void tampilkan(Profil profil) {
^
const &
kandidat-tidy.cpp:21:22: warning: use range-based for loop instead [modernize-loop-convert]
for (std::size_t i = 0; i < nilai.size(); ++i) {
^
kandidat-tidy.cpp:25:37: warning: use std::make_unique instead [modernize-make-unique]
std::unique_ptr<Profil> pemilik(new Profil{"Budi", 3});
^
std::make_unique<Profil>(
kandidat-tidy.cpp:26:22: warning: use nullptr instead of 0 [modernize-use-nullptr]
int* petunjuk = 0;
^
4 warnings generated.Empat temuan, dan masing-masing layak kita bedah:
performance-unnecessary-value-param(baris 14) — parameterProfil profildisalin utuh pada setiap pemanggilantampilkan, padahal function hanya membaca. Laporan bahkan menampilkan bentuk perbaikannya di bawah caret: ubah menjadiconst Profil&— salinan hilang, perilaku identik. Pada struct yang berisistd::string, biaya salinan ini bukan main-main di program yang memanggilnya ribuan kali.modernize-loop-convert(baris 21) — loop indeks manual bisa ditulis sebagai range-based for. Selain lebih pendek, versi range-based menghapus seluruh kelas bug tipe indeks (std::size_tversusint) yang sudah kita bahas pada chapter Compiler Warnings.modernize-make-unique(baris 25) —std::unique_ptr<Profil> pemilik(new Profil{...})memisahkan alokasi dan kepemilikan dalam dua langkah;std::make_unique<Profil>(...)menggabungkannya jadi satu ungkapan yang aman terhadap exception dan lebih singkat.modernize-use-nullptr(baris 26) —int* petunjuk = 0;sah, tetapi0juga nilai bilangan bulat sehingga bermakna ganda;nullptrhanya pointer dan membuat niat penulisnya tak terbantahkan.
Yang menarik dari kasus ini: tidak ada satu pun temuan yang membuat program salah hari ini. Itu justru karakter linting yang sehat — temuan-temuannya kecil secara individual, tetapi menumpuk menjadi perbedaan besar antara kode yang “berjalan” dan kode yang mudah dirawat.
Konfigurasi Proyek: Berkas .clang-tidy #
Mengetik -checks=... di setiap panggilan tidak mungkin bertahan lama. Seperti clang-format, clang-tidy membaca konfigurasi proyek dari satu berkas:
Checks: >
-*,
bugprone-*,
performance-*,
modernize-*,
readability-*,
-modernize-use-trailing-return-type
WarningsAsErrors: 'performance-*,bugprone-*'
FormatStyle: filePola bacanya: -* mematikan semua check bawaan lebih dulu, baris berikutnya menyalakan empat kategori pilihan kita, dan satu check dikecualikan (bentuk return auto yang disarankan check itu belum kita ajarkan di course ini — dan inilah pola yang benar untuk check yang tidak relevan bagi proyek: kecualikan secara spesifik, jangan matikan semuanya). WarningsAsErrors menaikkan dua kategori paling berbahaya menjadi error sehingga build gagal bila muncul, dan FormatStyle: file membuat perbaikan otomatis mengikuti .clang-format yang sama.
clang-tidy dalam Pipeline CI #
Di mesin lokal, clang-tidy paling cocok dijalankan pada berkas yang baru saja Anda ubah — laporannya langsung bisa ditindaklanjuti. Di CI, ia menjadi gerbang kolektif: satu job yang menjalankan clang-tidy atas seluruh proyek, dan pipeline gagal bila ada temuan yang dikonfigurasi sebagai error. Karena clang-tidy perlu tahu flag kompilasi yang dipakai proyek, proyek berbasis CMake menyediakannya lewat berkas compile_commands.json yang dirujuk dengan flag -p — jangan khawatir jika istilah itu belum familiar, kita akan mengujinya pada Section Build System dan Project Structure. Pola job CI-nya sendiri sederhana:
jobs:
tidy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Konfigurasi CMake untuk compile_commands.json
run: cmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
- name: clang-tidy
run: clang-tidy -p build $(git ls-files '*.cpp')Kombinasi inilah yang membuat kebiasaan ini bertahan: clang-format menjaga bentuk tetap seragam tanpa campur tangan manusia, clang-tidy menjaga pola-pola makna kecil tetap sehat, dan keduanya berjalan otomatis setiap ada perubahan. Namun ada satu kelas pertanyaan yang tidak bisa dijawab alat manapun sampai hari ini: apakah function konversiCelsiusKeFahrenheit benar-benar mengembalikan 212 saat menerima 100? Itu pertanyaan tentang perilaku, dan jawabannya hanya bisa datang dari kode yang menguji kode — unit testing, yang kita bangun pada chapter berikutnya.
Referensi: Clang — clang-tidy , Clang — Daftar check clang-tidy .