Skip to content

Commit 9d12a56

Browse files
Add clang-tidy support
Add a CMake option `ENABLE_CLANG_TIDY` to enable `clang-tidy` for the project. This is enabled on Linux only in the Github runners since it is the only image that has `clang-tidy` installed by default. I have disabled any check that didn't seem to improve the code. The only code that was exempt and should be fixed is the recursive function calls in `trimutil.cpp` triggered by `misc-no-recursion`. I think fixing them should be done separately to avoid an overly complex commit.
1 parent 8174a83 commit 9d12a56

13 files changed

+85
-29
lines changed

.clang-tidy

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
---
2+
Checks: "bugprone-*,
3+
misc-*,
4+
-misc-non-private-member-variables-in-classes,
5+
modernize-*,
6+
-modernize-use-default-member-init,
7+
-modernize-use-nodiscard,
8+
-modernize-use-trailing-return-type,
9+
performance-*,
10+
readability-*,
11+
-readability-convert-member-functions-to-static,
12+
-readability-else-after-return,
13+
-readability-implicit-bool-conversion,
14+
-readability-magic-numbers,
15+
-readability-named-parameter,
16+
-readability-redundant-member-init,
17+
-readability-qualified-auto,
18+
"
19+
WarningsAsErrors: "bugprone-*,
20+
misc-*,
21+
-misc-non-private-member-variables-in-classes,
22+
modernize-*,
23+
-modernize-use-default-member-init,
24+
-modernize-use-nodiscard,
25+
-modernize-use-trailing-return-type,
26+
performance-*,
27+
readability-*,
28+
-readability-convert-member-functions-to-static,
29+
-readability-else-after-return,
30+
-readability-implicit-bool-conversion,
31+
-readability-magic-numbers,
32+
-readability-named-parameter,
33+
-readability-redundant-member-init,
34+
-readability-qualified-auto,
35+
"
36+
HeaderFilterRegex: 'thirdparty/'
37+
FormatStyle: none

.github/workflows/ci.yaml

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ jobs:
3131
-DCMAKE_C_COMPILER=${{ matrix.environment.c }}
3232
${{ format('-D{0}={1}', matrix.environment.name == 'Windows' && 'CMAKE_CONFIGURATION_TYPES' || 'CMAKE_BUILD_TYPE', matrix.build_type) }}
3333
${{ matrix.environment.name != 'Windows' && '-G Ninja' || ''}}
34+
${{ matrix.environment.name == 'Linux' && '-DENABLE_CLANG_TIDY=ON' || ''}}
3435
- uses: elliotgoodrich/trimja-action@v1
3536
# Skip Windows (which doesn't yet use Ninja) and releases
3637
if: matrix.environment.name != 'Windows' && !startsWith(github.ref, 'refs/tags/')

CMakeLists.txt

+20
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ set(CMAKE_CXX_STANDARD 20)
66
set(CMAKE_CXX_STANDARD_REQUIRED ON)
77
set(CMAKE_COMPILE_WARNING_AS_ERROR ON)
88

9+
# Add an option to enable or disable clang-tidy
10+
option(ENABLE_CLANG_TIDY "Enable clang-tidy static analysis" OFF)
11+
12+
if(ENABLE_CLANG_TIDY)
13+
find_program(CLANG_TIDY_EXE NAMES "clang-tidy")
14+
if(CLANG_TIDY_EXE)
15+
set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE}")
16+
else()
17+
message(WARNING "clang-tidy not found. Static analysis will be skipped.")
18+
endif()
19+
endif()
20+
21+
922
###############################################################################
1023
# trimja #
1124
###############################################################################
@@ -34,6 +47,13 @@ add_executable(
3447
$<$<BOOL:${WIN32}>:thirdparty/ninja/getopt.c>
3548
)
3649

50+
set_source_files_properties(
51+
thirdparty/ninja/lexer.cc
52+
thirdparty/ninja/util.cc
53+
thirdparty/ninja/getopt.cc
54+
PROPERTIES SKIP_LINTING ON
55+
)
56+
3757
target_compile_definitions(trimja PRIVATE TRIMJA_VERSION="${CMAKE_PROJECT_VERSION}")
3858
target_include_directories(trimja PRIVATE src)
3959
target_include_directories(trimja SYSTEM PRIVATE thirdparty)

src/basicscope.cpp

+2-3
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ namespace trimja {
2626

2727
BasicScope::BasicScope() = default;
2828

29-
BasicScope::BasicScope(BasicScope&&) = default;
29+
BasicScope::BasicScope(BasicScope&&) noexcept = default;
3030

3131
BasicScope::BasicScope(const BasicScope& other) {
3232
for (const auto& [name, value] : other.m_variables) {
3333
m_variables.emplace(name.view(), value);
3434
}
3535
}
3636

37-
BasicScope& BasicScope::operator=(BasicScope&&) = default;
37+
BasicScope& BasicScope::operator=(BasicScope&&) noexcept = default;
3838

3939
BasicScope& BasicScope::operator=(const BasicScope& rhs) {
4040
BasicScope tmp{rhs};
@@ -64,6 +64,5 @@ bool BasicScope::appendValue(std::string& output, std::string_view name) const {
6464
output += it->second;
6565
return true;
6666
}
67-
}
6867

6968
} // namespace trimja

src/basicscope.h

+2-2
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ class BasicScope {
5252
* @brief Move constructor.
5353
* @param other The BasicScope to move from.
5454
*/
55-
BasicScope(BasicScope&& other);
55+
BasicScope(BasicScope&& other) noexcept;
5656

5757
/**
5858
* @brief Copy constructor.
@@ -65,7 +65,7 @@ class BasicScope {
6565
* @param rhs The BasicScope to move assign from.
6666
* @return A reference to the assigned BasicScope.
6767
*/
68-
BasicScope& operator=(BasicScope&& rhs);
68+
BasicScope& operator=(BasicScope&& rhs) noexcept;
6969

7070
/**
7171
* @brief Copy assignment operator.

src/cpuprofiler.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ Timer::~Timer() {
4646
stop();
4747
}
4848

49-
void Timer::stop() {
49+
void Timer::stop() { // NOLINT(readability-make-member-function-const)
5050
if (m_output) {
5151
*m_output = std::chrono::steady_clock::now() - m_start;
5252
}

src/depsreader.cpp

+7-8
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ namespace {
3636
const std::size_t NINJA_MAX_RECORD_SIZE = 0b11'1111'1111'1111'1111;
3737

3838
template <typename TYPE>
39-
TYPE readBinary(std::istream& in) {
39+
TYPE readFromStream(std::istream& in) {
4040
TYPE value;
4141
in.read(reinterpret_cast<char*>(&value), sizeof(value));
4242
return value;
@@ -86,7 +86,7 @@ DepsReader::DepsReader(const std::filesystem::path& ninja_deps)
8686
throw std::runtime_error("Unable to find ninjadeps signature");
8787
}
8888

89-
const std::int32_t header = readBinary<std::int32_t>(m_deps);
89+
const auto header = readFromStream<std::int32_t>(m_deps);
9090
if (header != 4) {
9191
throw std::runtime_error("Only version 4 supported of ninjadeps");
9292
}
@@ -97,7 +97,7 @@ bool DepsReader::read(std::variant<PathRecordView, DepsRecordView>* output) {
9797
if (m_deps.peek() == EOF) {
9898
return false;
9999
}
100-
const std::uint32_t rawRecordSize = readBinary<std::uint32_t>(m_deps);
100+
const auto rawRecordSize = readFromStream<std::uint32_t>(m_deps);
101101
const std::uint32_t recordSize = rawRecordSize & 0x7FFFFFFF;
102102
if (recordSize > NINJA_MAX_RECORD_SIZE) {
103103
throw std::runtime_error("Record exceeding the maximum size found");
@@ -110,19 +110,18 @@ bool DepsReader::read(std::variant<PathRecordView, DepsRecordView>* output) {
110110
const std::size_t padding =
111111
(end[-1] == '\0') + (end[-2] == '\0') + (end[-3] == '\0');
112112
m_storage.erase(m_storage.size() - padding);
113-
const std::uint32_t checksum = readBinary<std::uint32_t>(m_deps);
113+
const auto checksum = readFromStream<std::uint32_t>(m_deps);
114114
const std::int32_t id = ~checksum;
115115
*output = PathRecordView{id, m_storage};
116116
} else {
117-
const std::int32_t outIndex = readBinary<std::int32_t>(m_deps);
118-
const ninja_clock::time_point mtime =
119-
readBinary<ninja_clock::time_point>(m_deps);
117+
const auto outIndex = readFromStream<std::int32_t>(m_deps);
118+
const auto mtime = readFromStream<ninja_clock::time_point>(m_deps);
120119
const std::uint32_t numDependencies =
121120
(recordSize - sizeof(std::int32_t) - 2 * sizeof(std::uint32_t)) /
122121
sizeof(std::int32_t);
123122
m_depsStorage.resize(numDependencies);
124123
std::generate_n(m_depsStorage.begin(), numDependencies,
125-
[&] { return readBinary<std::int32_t>(m_deps); });
124+
[&] { return readFromStream<std::int32_t>(m_deps); });
126125
*output = DepsRecordView{outIndex, mtime, m_depsStorage};
127126
}
128127
} catch (const std::ios_base::failure& e) {

src/edgescope.cpp

+2-5
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@
2424

2525
#include <ninja/util.h>
2626

27-
namespace trimja {
28-
namespace detail {
27+
namespace trimja::detail {
2928

3029
void appendPaths(std::string& output,
3130
std::span<const std::string> paths,
@@ -44,6 +43,4 @@ void appendPaths(std::string& output,
4443
}
4544
}
4645

47-
} // namespace detail
48-
49-
} // namespace trimja
46+
} // namespace trimja::detail

src/evalstring.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ EvalString::Offset setLeadingBit(EvalString::Offset in) {
5858
}
5959

6060
bool hasLeadingBit(EvalString::Offset in) {
61-
return in >> (std::numeric_limits<EvalString::Offset>::digits - 1u);
61+
return in >> (std::numeric_limits<EvalString::Offset>::digits - 1U);
6262
}
6363

6464
} // namespace

src/graph.cpp

+3-3
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ std::size_t Graph::PathHash::operator()(const fixed_string& v) const {
3535
std::size_t Graph::PathHash::operator()(std::string_view v) const {
3636
// FNV-1a hash algorithm but on Windows we swap all backslashes with forward
3737
// slashes
38-
std::size_t hash = 14695981039346656037ull;
38+
std::size_t hash = 14695981039346656037ULL;
3939
for (const char ch : v) {
4040
#ifdef _WIN32
4141
hash ^= ch == '\\' ? '/' : ch;
4242
#else
4343
hash ^= ch;
4444
#endif
45-
hash *= 1099511628211ull;
45+
hash *= 1099511628211ULL;
4646
}
4747
return hash;
4848
}
@@ -132,7 +132,7 @@ std::size_t Graph::addDefault() {
132132
const std::size_t nextIndex = m_inputToOutput.size();
133133
m_inputToOutput.emplace_back();
134134
m_outputToInput.emplace_back();
135-
m_path.push_back("default");
135+
m_path.emplace_back("default");
136136
m_defaultIndex = nextIndex;
137137
return nextIndex;
138138
}

src/murmur_hash.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ std::uint64_t murmur_hash::hash(const void* key, std::size_t length) {
3434
// did too), to use a hard-coded seed that is used within `ninja`, and to
3535
// generally be brought up to modern C++ standards.
3636

37-
const unsigned char* data = static_cast<const unsigned char*>(key);
37+
const auto* data = static_cast<const unsigned char*>(key);
3838

3939
const std::uint64_t m = 0xc6a4'a793'5bd1'e995;
4040
const int r = 47;

src/trimja.m.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ branch, note the lone '-' argument to specify we are reading from stdin,
9696
9797
For more information visit the homepage https://github.com/elliotgoodrich/trimja)HELP";
9898

99-
static const option g_longOptions[] = {
99+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
100+
const option g_longOptions[] = {
100101
// TODO: Remove `--expected` and replace with comparing files within CTest
101102
{"builddir", no_argument, nullptr, 'b'},
102103
{"explain", no_argument, nullptr, 'e'},
@@ -163,7 +164,7 @@ bool instrumentMemory = false;
163164
bool explain = false;
164165
bool builddir = false;
165166

166-
int ch;
167+
int ch = -1;
167168
while ((ch = getopt_long(argc, argv, "a:f:ho:vw", g_longOptions, nullptr)) !=
168169
-1) {
169170
switch (ch) {

src/trimutil.cpp

+5-3
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ void parseDepFile(const std::filesystem::path& ninjaDeps,
663663
DepsReader(ninjaDeps)) {
664664
switch (record.index()) {
665665
case 0: {
666-
const PathRecordView& view = std::get<PathRecordView>(record);
666+
const auto& view = std::get<PathRecordView>(record);
667667
if (static_cast<std::size_t>(view.index) >= paths.size()) {
668668
paths.resize(view.index + 1);
669669
}
@@ -672,7 +672,7 @@ void parseDepFile(const std::filesystem::path& ninjaDeps,
672672
break;
673673
}
674674
case 1: {
675-
const DepsRecordView& view = std::get<DepsRecordView>(record);
675+
const auto& view = std::get<DepsRecordView>(record);
676676
if (static_cast<std::size_t>(view.outIndex) >= deps.size()) {
677677
deps.resize(view.outIndex + 1);
678678
}
@@ -772,6 +772,7 @@ void parseLogFile(const std::filesystem::path& ninjaLog,
772772
// If `index` has not been seen (using `seen`) then call
773773
// `markIfChildrenAffected` for all inputs to `index` and then set
774774
// `isAffected[index]` if any child is affected. Return whether this
775+
// NOLINTNEXTLINE(misc-no-recursion)
775776
void markIfChildrenAffected(std::size_t index,
776777
std::vector<bool>& seen,
777778
std::vector<bool>& isAffected,
@@ -814,7 +815,8 @@ void markIfChildrenAffected(std::size_t index,
814815

815816
// If `index` has not been seen (using `seen`) then call
816817
// `ifAffectedMarkAllChildren` for all outputs to `index` and then set
817-
// `isAffected[index]` if any child is affected. Return whether this
818+
// `isAffected[index]` if any child is affected.
819+
// NOLINTNEXTLINE(misc-no-recursion)
818820
void ifAffectedMarkAllChildren(std::size_t index,
819821
std::vector<bool>& seen,
820822
std::vector<bool>& isAffected,

0 commit comments

Comments
 (0)