ronantakizawa/codereview-bench huggingface dataset:
Repository: bitcoin/bitcoin
File: src/crypto/common.h (C/C++)

Code before:
// Copyright (c) 2014-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_COMMON_H
#define BITCOIN_CRYPTO_COMMON_H

#include <compat/endian.h>

#include <cstdint>
#include <cstring>

uint16_t static inline ReadLE16(const unsigned char* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return le16toh_internal(x);
}

uint32_t static inline ReadLE32(const unsigned char* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

uint64_t static inline ReadLE64(const unsigned char* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}

void static inline WriteLE16(unsigned char* ptr, uint16_t x)
{
    uint16_t v = htole16_internal(x);
    memcpy(ptr, &v, 2);
}

void static inline WriteLE32(unsigned char* ptr, uint32_t x)
{

Review comment (refactor):
Byte type means that size is 1, right? If you edit again, consider simplifying to something like:
```suggestion
template <typename B>
concept ByteType = (sizeof(B) == 1);
```

Or if you don't like it, to the mentioned `uint8_t`
```suggestion
template <typename B>
concept ByteType = std::same_as<B, uint8_t> || std::same_as<B, std::byte>;
```

Code after:
// Copyright (c) 2014-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_COMMON_H
#define BITCOIN_CRYPTO_COMMON_H

#include <compat/endian.h>

#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstring>

template <typename B>
concept ByteType = std::same_as<B, unsigned char> || std::same_as<B, std::byte>;

template <ByteType B>
inline uint16_t ReadLE16(const B* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return le16toh_internal(x);
}

template <ByteType B>
inline uint32_t ReadLE32(const B* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

template <ByteType B>
inline uint64_t ReadLE64(const B* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}
Repository: bitcoin/bitcoin
File: src/crypto/common.h (C/C++)

Code before:
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

uint64_t static inline ReadLE64(const unsigned char* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}

void static inline WriteLE16(unsigned char* ptr, uint16_t x)
{
    uint16_t v = htole16_internal(x);
    memcpy(ptr, &v, 2);
}

void static inline WriteLE32(unsigned char* ptr, uint32_t x)
{
    uint32_t v = htole32_internal(x);
    memcpy(ptr, &v, 4);
}

void static inline WriteLE64(unsigned char* ptr, uint64_t x)
{
    uint64_t v = htole64_internal(x);
    memcpy(ptr, &v, 8);
}

uint16_t static inline ReadBE16(const unsigned char* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return be16toh_internal(x);
}

uint32_t static inline ReadBE32(const unsigned char* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return be32toh_internal(x);
}

uint64_t static inline ReadBE64(const unsigned char* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return be64toh_internal(x);
}


Review comment (suggestion):
We could const the parameter and locals to obviate which one we're writing:
```suggestion
template <ByteType B>
void WriteLE16(B* ptr, const uint16_t x)
{
    const uint16_t v{htole16_internal(x)};
    memcpy(ptr, &v, 2);
}
```

Code after:
    memcpy(&x, ptr, 2);
    return le16toh_internal(x);
}

template <ByteType B>
inline uint32_t ReadLE32(const B* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

template <ByteType B>
inline uint64_t ReadLE64(const B* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}

template <ByteType B>
inline void WriteLE16(B* ptr, uint16_t x)
{
    uint16_t v = htole16_internal(x);
    memcpy(ptr, &v, 2);
}

template <ByteType B>
inline void WriteLE32(B* ptr, uint32_t x)
{
    uint32_t v = htole32_internal(x);
    memcpy(ptr, &v, 4);
}

template <ByteType B>
inline void WriteLE64(B* ptr, uint64_t x)
{
    uint64_t v = htole64_internal(x);
    memcpy(ptr, &v, 8);
}

template <ByteType B>
inline uint16_t ReadBE16(const B* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return be16toh_internal(x);
}

template <ByteType B>
inline uint32_t ReadBE32(const B* ptr)
Repository: bitcoin/bitcoin
File: src/crypto/common.h (C/C++)

Code before:
// Copyright (c) 2014-2020 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_COMMON_H
#define BITCOIN_CRYPTO_COMMON_H

#include <compat/endian.h>

#include <cstdint>
#include <cstring>

uint16_t static inline ReadLE16(const unsigned char* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return le16toh_internal(x);
}

uint32_t static inline ReadLE32(const unsigned char* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

uint64_t static inline ReadLE64(const unsigned char* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}

void static inline WriteLE16(unsigned char* ptr, uint16_t x)
{
    uint16_t v = htole16_internal(x);
    memcpy(ptr, &v, 2);
}

void static inline WriteLE32(unsigned char* ptr, uint32_t x)
{
    uint32_t v = htole32_internal(x);
    memcpy(ptr, &v, 4);
}

Review comment (refactor):
In commit "refactor: Allow std::byte in Read(LE/BE)" (fa83bec78ef3e86445e522afa396c97b58eb1902)

There is a shorter syntax for this that I think would make code easier to read (thoughout this file):

```diff
-template <ByteType B>
-inline uint16_t ReadLE16(const B* ptr)
+inline uint16_t ReadLE16(const ByteType auto* ptr)
```





Code after:
// Copyright (c) 2014-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_COMMON_H
#define BITCOIN_CRYPTO_COMMON_H

#include <compat/endian.h>

#include <concepts>
#include <cstddef>
#include <cstdint>
#include <cstring>

template <typename B>
concept ByteType = std::same_as<B, unsigned char> || std::same_as<B, std::byte>;

template <ByteType B>
inline uint16_t ReadLE16(const B* ptr)
{
    uint16_t x;
    memcpy(&x, ptr, 2);
    return le16toh_internal(x);
}

template <ByteType B>
inline uint32_t ReadLE32(const B* ptr)
{
    uint32_t x;
    memcpy(&x, ptr, 4);
    return le32toh_internal(x);
}

template <ByteType B>
inline uint64_t ReadLE64(const B* ptr)
{
    uint64_t x;
    memcpy(&x, ptr, 8);
    return le64toh_internal(x);
}

template <ByteType B>
inline void WriteLE16(B* ptr, uint16_t x)
{
Repository: bitcoin/bitcoin
File: src/arith_uint256.h (C/C++)

Code before:
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_ARITH_UINT256_H
#define BITCOIN_ARITH_UINT256_H

#include <cstdint>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <string>

class uint256;

class uint_error : public std::runtime_error {
public:
    explicit uint_error(const std::string& str) : std::runtime_error(str) {}
};

/** Template base class for unsigned big integers. */
template<unsigned int BITS>
class base_uint
{
protected:
    static_assert(BITS / 32 > 0 && BITS % 32 == 0, "Template parameter BITS must be a positive multiple of 32.");
    static constexpr int WIDTH = BITS / 32;
    /** Number represented in least-significant-first base 2^32 digits. */
    uint32_t pn[WIDTH];
public:

    base_uint()
    {
        for (int i = 0; i < WIDTH; i++)
            pn[i] = 0;
    }

    base_uint(const base_uint& b)
    {
        for (int i = 0; i < WIDTH; i++)
            pn[i] = b.pn[i];
    }

    base_uint& operator=(const base_uint& b)
    {
        if (this != &b) {
            for (int i = 0; i < WIDTH; i++)
                pn[i] = b.pn[i];
        }
        return *this;
    }

    base_uint(uint64_t b)

Review comment (suggestion):
I had to read this several times (didn't understand what a "least-significant-first base" is at first), consider the following:
```suggestion
    /** Integer with 32-bit digits, least-significant first. */
```

Code after:
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_ARITH_UINT256_H
#define BITCOIN_ARITH_UINT256_H

#include <cstdint>
#include <cstring>
#include <limits>
#include <stdexcept>
#include <string>

class uint256;

class uint_error : public std::runtime_error {
public:
    explicit uint_error(const std::string& str) : std::runtime_error(str) {}
};

/** Template base class for unsigned big integers. */
template<unsigned int BITS>
class base_uint
{
protected:
    static_assert(BITS / 32 > 0 && BITS % 32 == 0, "Template parameter BITS must be a positive multiple of 32.");
    static constexpr int WIDTH = BITS / 32;
    /** Big integer represented with 32-bit digits, least-significant first. */
    uint32_t pn[WIDTH];
public:

    base_uint()
    {
        for (int i = 0; i < WIDTH; i++)
            pn[i] = 0;
    }

    base_uint(const base_uint& b)
    {
        for (int i = 0; i < WIDTH; i++)
            pn[i] = b.pn[i];
    }

    base_uint& operator=(const base_uint& b)
    {
        if (this != &b) {
            for (int i = 0; i < WIDTH; i++)
                pn[i] = b.pn[i];
        }
        return *this;
    }

    base_uint(uint64_t b)
Repository: bitcoin/bitcoin
File: src/policy/packages.h (C/C++)

Code before:
 *
 * @returns true if there are no conflicts. False if any two transactions spend the same prevout.
 * */
bool IsConsistentPackage(const Package& txns);

/** Context-free package policy checks:
 * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
 * 2. The total weight cannot exceed MAX_PACKAGE_WEIGHT.
 * 3. If any dependencies exist between transactions, parents must appear before children.
 * 4. Transactions cannot conflict, i.e., spend the same inputs.
 */
bool IsWellFormedPackage(const Package& txns, PackageValidationState& state, bool require_sorted);

/** Context-free check that a package is exactly one child and its parents; not all parents need to
 * be present, but the package must not contain any transactions that are not the child's parents.
 * It is expected to be sorted, which means the last transaction must be the child.
 */
bool IsChildWithParents(const Package& package);

/** Context-free check that a package IsChildWithParents() and none of the parents depend on each
 * other (the package is a "tree").
 */
bool IsChildWithParentsTree(const Package& package);

/** Get the hash of these transactions' wtxids, concatenated in lexicographical order (treating the
 * wtxids as little endian encoded uint256, smallest to largest). */
uint256 GetPackageHash(const std::vector<CTransactionRef>& transactions);

#endif // BITCOIN_POLICY_PACKAGES_H


Review comment (suggestion):
```suggestion
 * treated as little-endian numbers and sorted in ascending numeric order.
```

Code after:
 *
 * @returns true if there are no conflicts. False if any two transactions spend the same prevout.
 * */
bool IsConsistentPackage(const Package& txns);

/** Context-free package policy checks:
 * 1. The number of transactions cannot exceed MAX_PACKAGE_COUNT.
 * 2. The total weight cannot exceed MAX_PACKAGE_WEIGHT.
 * 3. If any dependencies exist between transactions, parents must appear before children.
 * 4. Transactions cannot conflict, i.e., spend the same inputs.
 */
bool IsWellFormedPackage(const Package& txns, PackageValidationState& state, bool require_sorted);

/** Context-free check that a package is exactly one child and its parents; not all parents need to
 * be present, but the package must not contain any transactions that are not the child's parents.
 * It is expected to be sorted, which means the last transaction must be the child.
 */
bool IsChildWithParents(const Package& package);

/** Context-free check that a package IsChildWithParents() and none of the parents depend on each
 * other (the package is a "tree").
 */
bool IsChildWithParentsTree(const Package& package);

/** Get the hash of the concatenated wtxids of transactions, with wtxids
 * treated as a little-endian numbers and sorted in ascending numeric order.
 */
uint256 GetPackageHash(const std::vector<CTransactionRef>& transactions);

#endif // BITCOIN_POLICY_PACKAGES_H
Repository: bitcoin/bitcoin
File: src/kernel/caches.h (C/C++)

Code before:
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_KERNEL_CACHES_H
#define BITCOIN_KERNEL_CACHES_H

#include <util/byte_units.h>

#include <algorithm>

//! Suggested default amount of cache reserved for the kernel (bytes)
static constexpr size_t DEFAULT_KERNEL_CACHE{450_MiB};
//! Max memory allocated to block tree DB specific cache (bytes)
static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB};
//! Max memory allocated to coin DB specific cache (bytes)
static constexpr size_t MAX_COINS_DB_CACHE{8_MiB};

namespace kernel {
struct CacheSizes {
    size_t block_tree_db;
    size_t coins_db;
    size_t coins;

    CacheSizes(size_t total_cache)
    {
        block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
        total_cache -= block_tree_db;
        coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
        total_cache -= coins_db;
        coins = total_cache; // the rest goes to the coins cache
    }
};
} // namespace kernel

#endif // BITCOIN_KERNEL_CACHES_H


Review comment (performance):
In commit "coins: introduce dynamic batch size calculator based on dbcache value" (b249239c1747a5425e5161974d2b9c9c5be17dab)

Maybe there should be a test or static assert somewhere that `DEFAULT_DB_CACHE_BATCH == GetDbBatchSize(DEFAULT_KERNEL_CACHE)`

Code after:
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_KERNEL_CACHES_H
#define BITCOIN_KERNEL_CACHES_H

#include <util/byte_units.h>

#include <algorithm>

//! Suggested default amount of cache reserved for the kernel (bytes)
static constexpr size_t DEFAULT_KERNEL_CACHE{450_MiB};
//! Default LevelDB write batch size
static constexpr size_t DEFAULT_DB_CACHE_BATCH{32_MiB};

//! Max memory allocated to block tree DB specific cache (bytes)
static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB};
//! Max memory allocated to coin DB specific cache (bytes)
static constexpr size_t MAX_COINS_DB_CACHE{8_MiB};

namespace kernel {
struct CacheSizes {
    size_t block_tree_db;
    size_t coins_db;
    size_t coins;

    CacheSizes(size_t total_cache)
    {
        block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
        total_cache -= block_tree_db;
        coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
        total_cache -= coins_db;
        coins = total_cache; // the rest goes to the coins cache
    }
};
} // namespace kernel

#endif // BITCOIN_KERNEL_CACHES_H
Repository: bitcoin/bitcoin
File: src/kernel/caches.h (C/C++)

Code before:
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_KERNEL_CACHES_H
#define BITCOIN_KERNEL_CACHES_H

#include <util/byte_units.h>

#include <algorithm>

//! Suggested default amount of cache reserved for the kernel (bytes)
static constexpr size_t DEFAULT_KERNEL_CACHE{450_MiB};
//! Max memory allocated to block tree DB specific cache (bytes)
static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB};
//! Max memory allocated to coin DB specific cache (bytes)
static constexpr size_t MAX_COINS_DB_CACHE{8_MiB};

namespace kernel {
struct CacheSizes {
    size_t block_tree_db;
    size_t coins_db;
    size_t coins;

    CacheSizes(size_t total_cache)
    {
        block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
        total_cache -= block_tree_db;
        coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
        total_cache -= coins_db;
        coins = total_cache; // the rest goes to the coins cache
    }
};
} // namespace kernel

#endif // BITCOIN_KERNEL_CACHES_H


Review comment (performance):
In commit "coins: introduce dynamic batch size calculator based on dbcache value" (b249239c1747a5425e5161974d2b9c9c5be17dab)

Name suggests this is a minimum size, but value is actually ignored if `-dbbatchsize` is specified, so is not an enforced minimum. Would suggest calling it a "minimum default" value instead of a "minimum" value to be more accurate. Or it might be better to just drop `MIN_DB_CACHE_BATCH` and `MAX_DB_CACHE_BATCH` here and just inline these in the `GetDbBatchSize` function since they are not used anywhere outside it.




Code after:
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_KERNEL_CACHES_H
#define BITCOIN_KERNEL_CACHES_H

#include <util/byte_units.h>

#include <algorithm>

//! Suggested default amount of cache reserved for the kernel (bytes)
static constexpr size_t DEFAULT_KERNEL_CACHE{450_MiB};
//! Default LevelDB write batch size
static constexpr size_t DEFAULT_DB_CACHE_BATCH{32_MiB};

//! Max memory allocated to block tree DB specific cache (bytes)
static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB};
//! Max memory allocated to coin DB specific cache (bytes)
static constexpr size_t MAX_COINS_DB_CACHE{8_MiB};

namespace kernel {
struct CacheSizes {
    size_t block_tree_db;
    size_t coins_db;
    size_t coins;

    CacheSizes(size_t total_cache)
    {
        block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
        total_cache -= block_tree_db;
        coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
        total_cache -= coins_db;
        coins = total_cache; // the rest goes to the coins cache
    }
};
} // namespace kernel

#endif // BITCOIN_KERNEL_CACHES_H
Repository: bitcoin/bitcoin
File: src/crypto/hex_base.h (C/C++)

Code before:
// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_HEX_BASE_H
#define BITCOIN_CRYPTO_HEX_BASE_H

#include <span.h>

#include <cstddef>
#include <cstdint>
#include <span>
#include <string>

/**
 * Convert a span of bytes to a lower-case hexadecimal string.
 */
std::string HexStr(const std::span<const uint8_t> s);
inline std::string HexStr(const std::span<const char> s) { return HexStr(MakeUCharSpan(s)); }
inline std::string HexStr(const std::span<const std::byte> s) { return HexStr(MakeUCharSpan(s)); }

signed char HexDigit(char c);

#endif // BITCOIN_CRYPTO_HEX_BASE_H


Review comment (refactor):
In commit "refactor: Enforce readability-avoid-const-params-in-decls" (fa8e57951abc5c737ab68694b8f6aa77551faa6b)

Since these are definitions not declarations, I don't think these actually need to change to fix the warnings. Does seem good to change, though.

Code after:
// Copyright (c) 2009-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_CRYPTO_HEX_BASE_H
#define BITCOIN_CRYPTO_HEX_BASE_H

#include <span.h>

#include <cstddef>
#include <cstdint>
#include <span>
#include <string>

/**
 * Convert a span of bytes to a lower-case hexadecimal string.
 */
std::string HexStr(std::span<const uint8_t> s);
inline std::string HexStr(std::span<const char> s) { return HexStr(MakeUCharSpan(s)); }
inline std::string HexStr(std::span<const std::byte> s) { return HexStr(MakeUCharSpan(s)); }

signed char HexDigit(char c);

#endif // BITCOIN_CRYPTO_HEX_BASE_H
Repository: bitcoin/bitcoin
File: test/lint/test_runner/src/main.rs (Rust)

Code before:
                .success()
        {
            good = false;
            println!("^---- ⚠️ Failure generated from {}", entry_fn);
        }
    }
    if good {
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn main() -> ExitCode {
    let linters_to_run: Vec<&Linter> = if env::args().count() > 1 {
        let args: Vec<String> = env::args().skip(1).collect();
        parse_lint_args(&args)
    } else {
        // If no arguments are passed, run all linters.
        get_linter_list()
    };

    let git_root = get_git_root();
    let commit_range = commit_range();
    let commit_log = check_output(git().args(["log", "--no-merges", "--oneline", &commit_range]))
        .expect("git error");
    println!("Checking commit range ({commit_range}):\n{commit_log}\n");

    let mut test_failed = false;
    for linter in linters_to_run {
        // chdir to root before each lint test
        env::set_current_dir(&git_root).unwrap();
        if let Err(err) = (linter.lint_fn)() {
            println!(
                "^^^\n{err}\n^---- ⚠️ Failure generated from lint check '{}' ({})!\n\n",
                linter.name, linter.description,
            );
            test_failed = true;
        }
    }
    if test_failed {
        ExitCode::FAILURE
    } else {
        ExitCode::SUCCESS
    }
}


Review comment (bug):
nit: 
In `commit_range` there is also a `.expect` which has the text "git command failed"
it would make sense to have the same text for consistency, but feel free to ignore

Code after:
                .success()
        {
            good = false;
            println!("^---- ⚠️ Failure generated from {}", entry_fn);
        }
    }
    if good {
        Ok(())
    } else {
        Err("".to_string())
    }
}

fn main() -> ExitCode {
    let linters_to_run: Vec<&Linter> = if env::args().count() > 1 {
        let args: Vec<String> = env::args().skip(1).collect();
        parse_lint_args(&args)
    } else {
        // If no arguments are passed, run all linters.
        get_linter_list()
    };

    let git_root = get_git_root();
    let commit_range = commit_range();
    let commit_log = check_output(git().args(["log", "--no-merges", "--oneline", &commit_range]))
        .expect("check_output failed");
    println!("Checking commit range ({commit_range}):\n{commit_log}\n");

    let mut test_failed = false;
    for linter in linters_to_run {
        // chdir to root before each lint test
        env::set_current_dir(&git_root).unwrap();
        if let Err(err) = (linter.lint_fn)() {
            println!(
                "^^^\n{err}\n^---- ⚠️ Failure generated from lint check '{}' ({})!\n\n",
                linter.name, linter.description,
            );
            test_failed = true;
        }
    }
    if test_failed {
        ExitCode::FAILURE
    } else {
        ExitCode::SUCCESS
    }
}