Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(ci): Add clang-tidy checks and ensure they pass #639

Merged
merged 22 commits into from
Oct 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
---
# Disable valist, it's buggy: https://github.com/llvm/llvm-project/issues/40656
# Disable DeprecatedOrUnsafeBufferHandling because it suggests we replace
# memset and memcpy with memset_s() and memcpy_s() if compiled with C11. Because
# we also support C99, we can't blindly replace those calls.
Checks: '-clang-analyzer-valist.Uninitialized,-clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling'
FormatStyle: google
77 changes: 77 additions & 0 deletions .github/workflows/clang-tidy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: clang-tidy

on:
push:
branches:
- main
pull_request:
branches:
- main
paths:
- 'CMakeLists.txt'
- '.github/workflows/clang-tidy.yaml'
- 'src/nanoarrow/**'

permissions:
contents: read

jobs:
clang-tidy:

runs-on: ubuntu-latest

name: ${{ matrix.config.label }}

steps:
- uses: actions/checkout@v4

- name: Cache Arrow C++ Build
id: cache-arrow-build
uses: actions/cache@v4
with:
path: arrow
# Bump the number at the end of this line to force a new Arrow C++ build
key: arrow-${{ runner.os }}-${{ runner.arch }}-1

- name: Build Arrow C++
if: steps.cache-arrow-build.outputs.cache-hit != 'true'
shell: bash
run: |
ci/scripts/build-arrow-cpp-minimal.sh 15.0.2 arrow

- name: Build nanoarrow
run: |
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`/dist/lib
sudo ldconfig

ARROW_PATH="$(pwd)/arrow"
mkdir build
cd build

cmake .. -DNANOARROW_DEVICE=ON -DNANOARROW_IPC=ON \
-DNANOARROW_BUILD_TESTS=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=ON \
-DCMAKE_PREFIX_PATH="${ARROW_PATH}"

cmake --build .

- name: Run clang-tidy
run: |
ci/scripts/run-clang-tidy.sh . build/
48 changes: 48 additions & 0 deletions ci/scripts/run-clang-tidy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bash
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

set -e

main() {
local -r source_dir="${1}"
local -r build_dir="${2}"

if [ $(uname) = "Darwin" ]; then
local -r jobs=$(sysctl -n hw.ncpu)
else
local -r jobs=$(nproc)
fi

set -x

run-clang-tidy -p "${build_dir}" -j$jobs \
-extra-arg=-Wno-unknown-warning-option | \
tee "${build_dir}/clang-tidy-output.txt"

if grep -e "warning:" -e "error:" "${build_dir}/clang-tidy-output.txt"; then
echo "Warnings or errors found!"
exit 1
else
echo "No warnings or errors found!"
fi

set +x
}

main "$@"
4 changes: 3 additions & 1 deletion src/nanoarrow/common/array.c
Original file line number Diff line number Diff line change
Expand Up @@ -842,7 +842,9 @@ static int ArrowArrayViewValidateMinimal(struct ArrowArrayView* array_view,
for (int i = 0; i < 2; i++) {
int64_t element_size_bytes = array_view->layout.element_size_bits[i] / 8;
// Initialize with a value that will cause an error if accidentally used uninitialized
int64_t min_buffer_size_bytes = array_view->buffer_views[i].size_bytes + 1;
// Need to suppress the clang-tidy warning because gcc warns for possible use
int64_t min_buffer_size_bytes = // NOLINT(clang-analyzer-deadcode.DeadStores)
array_view->buffer_views[i].size_bytes + 1;

switch (array_view->layout.buffer_type[i]) {
case NANOARROW_BUFFER_TYPE_VALIDITY:
Expand Down
13 changes: 13 additions & 0 deletions src/nanoarrow/common/inline_buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ static inline ArrowErrorCode ArrowBufferReserve(struct ArrowBuffer* buffer,
static inline void ArrowBufferAppendUnsafe(struct ArrowBuffer* buffer, const void* data,
int64_t size_bytes) {
if (size_bytes > 0) {
NANOARROW_DCHECK(buffer->data != NULL);
memcpy(buffer->data + buffer->size_bytes, data, size_bytes);
buffer->size_bytes += size_bytes;
}
Expand Down Expand Up @@ -295,8 +296,10 @@ static inline ArrowErrorCode ArrowBufferAppendFill(struct ArrowBuffer* buffer,

NANOARROW_RETURN_NOT_OK(ArrowBufferReserve(buffer, size_bytes));

NANOARROW_DCHECK(buffer->data != NULL); // To help clang-tidy
memset(buffer->data + buffer->size_bytes, value, size_bytes);
buffer->size_bytes += size_bytes;

return NANOARROW_OK;
}

Expand Down Expand Up @@ -413,6 +416,8 @@ static inline void ArrowBitsUnpackInt32(const uint8_t* bits, int64_t start_offse
return;
}

NANOARROW_DCHECK(bits != NULL && out != NULL);

const int64_t i_begin = start_offset;
const int64_t i_end = start_offset + length;
const int64_t i_last_valid = i_end - 1;
Expand Down Expand Up @@ -461,6 +466,12 @@ static inline void ArrowBitSetTo(uint8_t* bits, int64_t i, uint8_t bit_is_set) {

static inline void ArrowBitsSetTo(uint8_t* bits, int64_t start_offset, int64_t length,
uint8_t bits_are_set) {
if (length == 0) {
return;
}

NANOARROW_DCHECK(bits != NULL);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since bits is a parameter this might also be a good use case for the clang/gcc nonnull attribute

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't know about that! I think it can be NULL if length == 0 here but there are other places where it could be useful!


const int64_t i_begin = start_offset;
const int64_t i_end = start_offset + length;
const uint8_t fill_byte = (uint8_t)(-bits_are_set);
Expand Down Expand Up @@ -504,6 +515,8 @@ static inline int64_t ArrowBitCountSet(const uint8_t* bits, int64_t start_offset
return 0;
}

NANOARROW_DCHECK(bits != NULL);

const int64_t i_begin = start_offset;
const int64_t i_end = start_offset + length;
const int64_t i_last_valid = i_end - 1;
Expand Down
17 changes: 9 additions & 8 deletions src/nanoarrow/common/nanoarrow_hpp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayTest) {

// move constructor
nanoarrow::UniqueArray array2 = std::move(array);
EXPECT_EQ(array->release, nullptr);
EXPECT_EQ(array->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_NE(array2->release, nullptr);
EXPECT_EQ(array2->length, 1);

Expand All @@ -71,7 +71,7 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueSchemaTest) {

// move constructor
nanoarrow::UniqueSchema schema2 = std::move(schema);
EXPECT_EQ(schema->release, nullptr);
EXPECT_EQ(schema->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_NE(schema2->release, nullptr);
EXPECT_STREQ(schema2->format, "i");

Expand Down Expand Up @@ -101,7 +101,7 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayStreamTest) {

// move constructor
nanoarrow::UniqueArrayStream array_stream2 = std::move(array_stream);
EXPECT_EQ(array_stream->release, nullptr);
EXPECT_EQ(array_stream->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_NE(array_stream2->release, nullptr);
EXPECT_EQ(ArrowArrayStreamGetSchema(array_stream2.get(), schema.get(), nullptr),
NANOARROW_OK);
Expand Down Expand Up @@ -137,8 +137,8 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueBufferTest) {

// move constructor
nanoarrow::UniqueBuffer buffer2 = std::move(buffer);
EXPECT_EQ(buffer->data, nullptr);
EXPECT_EQ(buffer->size_bytes, 0);
EXPECT_EQ(buffer->data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_EQ(buffer->size_bytes, 0); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_NE(buffer2->data, nullptr);
EXPECT_EQ(buffer2->size_bytes, 123);

Expand All @@ -161,8 +161,8 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueBitmapTest) {

// move constructor
nanoarrow::UniqueBitmap bitmap2 = std::move(bitmap);
EXPECT_EQ(bitmap->buffer.data, nullptr);
EXPECT_EQ(bitmap->size_bits, 0);
EXPECT_EQ(bitmap->buffer.data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_EQ(bitmap->size_bits, 0); // NOLINT(clang-analyzer-cplusplus.Move)
EXPECT_NE(bitmap2->buffer.data, nullptr);
EXPECT_EQ(bitmap2->size_bits, 123);

Expand Down Expand Up @@ -250,7 +250,8 @@ TEST(NanoarrowHppTest, NanoarrowHppUniqueArrayViewTest) {

// move constructor
nanoarrow::UniqueArrayView array_view2 = std::move(array_view);
EXPECT_EQ(array_view->storage_type, NANOARROW_TYPE_UNINITIALIZED);
EXPECT_EQ(array_view->storage_type, // NOLINT(clang-analyzer-cplusplus.Move)
NANOARROW_TYPE_UNINITIALIZED);
EXPECT_EQ(array_view2->storage_type, NANOARROW_TYPE_STRUCT);

// pointer constructor
Expand Down
2 changes: 1 addition & 1 deletion src/nanoarrow/common/schema.c
Original file line number Diff line number Diff line change
Expand Up @@ -1346,7 +1346,7 @@ static inline void ArrowToStringLogChars(char** out, int64_t n_chars_last,
// In the unlikely snprintf() returning a negative value (encoding error),
// ensure the result won't cause an out-of-bounds access.
if (n_chars_last < 0) {
n_chars = 0;
n_chars_last = 0;
}

*n_chars += n_chars_last;
Expand Down
1 change: 1 addition & 0 deletions src/nanoarrow/common/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ ArrowErrorCode ArrowDecimalSetDigits(struct ArrowDecimal* decimal,
// https://github.com/apache/arrow/blob/cd3321b28b0c9703e5d7105d6146c1270bbadd7f/cpp/src/arrow/util/decimal.cc#L365
ArrowErrorCode ArrowDecimalAppendDigitsToBuffer(const struct ArrowDecimal* decimal,
struct ArrowBuffer* buffer) {
NANOARROW_DCHECK(decimal->n_words == 2 || decimal->n_words == 4);
int is_negative = ArrowDecimalSign(decimal) < 0;

uint64_t words_little_endian[4];
Expand Down
2 changes: 1 addition & 1 deletion src/nanoarrow/device/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ static ArrowErrorCode ArrowDeviceArrayViewEnsureBufferSizesAsync(
NANOARROW_DCHECK(cursor == (buffer.data + buffer.size_bytes));
ArrowBufferReset(&buffer);

return NANOARROW_OK;
return result;
}

ArrowErrorCode ArrowDeviceArrayViewSetArrayAsync(
Expand Down
9 changes: 5 additions & 4 deletions src/nanoarrow/device/device_hpp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ TEST(NanoarrowDeviceHpp, UniqueDeviceArray) {
ASSERT_NE(array->array.release, nullptr);

nanoarrow::device::UniqueDeviceArray array2 = std::move(array);
ASSERT_EQ(array->array.release, nullptr);
ASSERT_EQ(array->array.release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
ASSERT_NE(array2->array.release, nullptr);
}

Expand All @@ -46,7 +46,7 @@ TEST(NanoarrowDeviceHpp, UniqueDeviceArrayStream) {
ASSERT_NE(stream->release, nullptr);

nanoarrow::device::UniqueDeviceArrayStream stream2 = std::move(stream);
ASSERT_EQ(stream->release, nullptr);
ASSERT_EQ(stream->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
ASSERT_NE(stream2->release, nullptr);
}

Expand All @@ -57,7 +57,7 @@ TEST(NanoarrowDeviceHpp, UniqueDevice) {
ArrowDeviceInitCpu(device.get());

nanoarrow::device::UniqueDevice device2 = std::move(device);
ASSERT_EQ(device->release, nullptr);
ASSERT_EQ(device->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
ASSERT_NE(device2->release, nullptr);
}

Expand All @@ -71,5 +71,6 @@ TEST(NanoarrowDeviceHpp, UniqueDeviceArrayView) {

nanoarrow::device::UniqueDeviceArrayView array_view2 = std::move(array_view);
ASSERT_EQ(array_view2->array_view.storage_type, NANOARROW_TYPE_INT32);
ASSERT_EQ(array_view->array_view.storage_type, NANOARROW_TYPE_UNINITIALIZED);
ASSERT_EQ(array_view->array_view.storage_type, // NOLINT(clang-analyzer-cplusplus.Move)
NANOARROW_TYPE_UNINITIALIZED);
}
1 change: 0 additions & 1 deletion src/nanoarrow/device/metal_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ TEST(NanoarrowDeviceMetal, DeviceGpuBufferMove) {
struct ArrowBufferView view = {data, sizeof(data)};

ASSERT_EQ(ArrowDeviceBufferInit(cpu, view, gpu, &buffer), NANOARROW_OK);
auto mtl_buffer = reinterpret_cast<MTL::Buffer*>(buffer.data);

// GPU -> GPU: just a move
uint8_t* old_ptr = buffer.data;
Expand Down
4 changes: 4 additions & 0 deletions src/nanoarrow/ipc/decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,9 @@ struct ArrowIpcIntervalMonthDayNano {
static int ArrowIpcDecoderSwapEndian(struct ArrowIpcBufferSource* src,
struct ArrowBufferView* out_view,
struct ArrowBuffer* dst, struct ArrowError* error) {
NANOARROW_DCHECK(out_view->size_bytes > 0);
NANOARROW_DCHECK(out_view->data.data != NULL);

// Some buffer data types don't need any endian swapping
switch (src->data_type) {
case NANOARROW_TYPE_BOOL:
Expand Down Expand Up @@ -1539,6 +1542,7 @@ static int ArrowIpcDecoderSwapEndian(struct ArrowIpcBufferSource* src,
uint64_t* ptr_dst = (uint64_t*)dst->data;
uint64_t words[4];
int n_words = (int)(src->element_size_bits / 64);
NANOARROW_DCHECK(n_words == 2 || n_words == 4);

for (int64_t i = 0; i < (dst->size_bytes / n_words / 8); i++) {
for (int j = 0; j < n_words; j++) {
Expand Down
8 changes: 4 additions & 4 deletions src/nanoarrow/ipc/ipc_hpp_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueDecoder) {

nanoarrow::ipc::UniqueDecoder decoder2 = std::move(decoder);
EXPECT_NE(decoder2->private_data, nullptr);
EXPECT_EQ(decoder->private_data, nullptr);
EXPECT_EQ(decoder->private_data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
}

TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueEncoder) {
Expand All @@ -40,7 +40,7 @@ TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueEncoder) {

nanoarrow::ipc::UniqueEncoder encoder2 = std::move(encoder);
EXPECT_NE(encoder2->private_data, nullptr);
EXPECT_EQ(encoder->private_data, nullptr);
EXPECT_EQ(encoder->private_data, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
}

TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueInputStream) {
Expand All @@ -54,7 +54,7 @@ TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueInputStream) {

nanoarrow::ipc::UniqueInputStream input2 = std::move(input);
EXPECT_NE(input2->release, nullptr);
EXPECT_EQ(input->release, nullptr);
EXPECT_EQ(input->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
}

TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueOutputStream) {
Expand All @@ -68,5 +68,5 @@ TEST(NanoarrowIpcHppTest, NanoarrowIpcHppTestUniqueOutputStream) {

nanoarrow::ipc::UniqueOutputStream output2 = std::move(output);
EXPECT_NE(output2->release, nullptr);
EXPECT_EQ(output->release, nullptr);
EXPECT_EQ(output->release, nullptr); // NOLINT(clang-analyzer-cplusplus.Move)
}
Loading
Loading