Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
17 changes: 17 additions & 0 deletions .github/workflows/clang-tidy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,20 @@ jobs:
name: clang-tidy-report
path: build/clang-tidy-report.txt
retention-days: 14

- name: Convert clang-tidy report to Allure JUnit XML
if: always()
run: |
THRESHOLD=$(head -1 clang-tidy-baseline.txt 2>/dev/null | tr -d '[:space:]')
python3 scripts/clang_tidy_to_allure.py \
build/clang-tidy-report.txt \
allure-results \
${THRESHOLD:+--threshold "$THRESHOLD"}

- name: Upload Allure results
uses: actions/upload-artifact@v4
if: always()
with:
name: allure-results-clang-tidy
path: allure-results/
retention-days: 14
2 changes: 1 addition & 1 deletion clang-tidy-baseline.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1783
384
8 changes: 4 additions & 4 deletions include/core/session_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,14 +130,14 @@ class SessionManager {
*/
size_t get_active_session_count() const;

// Prevent copying
SessionManager(const SessionManager&) = delete;
SessionManager& operator=(const SessionManager&) = delete;

private:
std::unordered_map<uint16_t, std::shared_ptr<Session>> sessions_;
mutable platform::Mutex sessions_mutex_;
uint16_t next_session_id_{1};

// Prevent copying
SessionManager(const SessionManager&) = delete;
SessionManager& operator=(const SessionManager&) = delete;
};

} // namespace someip
Expand Down
5 changes: 3 additions & 2 deletions include/e2e/e2e_profile_registry.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,12 @@ class E2EProfileRegistry {
*/
E2EProfile* get_default_profile();

E2EProfileRegistry(const E2EProfileRegistry&) = delete;
E2EProfileRegistry& operator=(const E2EProfileRegistry&) = delete;

private:
E2EProfileRegistry() = default;
~E2EProfileRegistry() = default;
E2EProfileRegistry(const E2EProfileRegistry&) = delete;
E2EProfileRegistry& operator=(const E2EProfileRegistry&) = delete;

mutable platform::Mutex mutex_;
std::unordered_map<uint32_t, E2EProfilePtr> profiles_by_id_;
Expand Down
2 changes: 1 addition & 1 deletion include/platform/posix/net_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ static inline int someip_set_socket_timeout(someip_socket_t fd, int optname,
int timeout_ms) {
struct timeval tv{};
tv.tv_sec = timeout_ms / 1000;
tv.tv_usec = (timeout_ms % 1000) * 1000;
tv.tv_usec = static_cast<long>(timeout_ms % 1000) * 1000L;
return ::setsockopt(fd, SOL_SOCKET, optname, &tv, sizeof(tv));
}

Expand Down
8 changes: 4 additions & 4 deletions include/someip/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,11 @@ struct MessageId {
MessageId(uint16_t service, uint16_t method) : service_id(service), method_id(method) {}

uint32_t to_uint32() const {
return (static_cast<uint32_t>(service_id) << 16) | static_cast<uint32_t>(method_id);
return (static_cast<uint32_t>(service_id) << 16U) | static_cast<uint32_t>(method_id);
}

static MessageId from_uint32(uint32_t value) {
return MessageId(static_cast<uint16_t>(value >> 16), static_cast<uint16_t>(value & 0xFFFFU));
return MessageId(static_cast<uint16_t>(value >> 16U), static_cast<uint16_t>(value & 0xFFFFU));
}

bool operator==(const MessageId& other) const {
Expand All @@ -103,11 +103,11 @@ struct RequestId {
RequestId(uint16_t client, uint16_t session) : client_id(client), session_id(session) {}

uint32_t to_uint32() const {
return (static_cast<uint32_t>(client_id) << 16) | static_cast<uint32_t>(session_id);
return (static_cast<uint32_t>(client_id) << 16U) | static_cast<uint32_t>(session_id);
}

static RequestId from_uint32(uint32_t value) {
return RequestId(static_cast<uint16_t>(value >> 16), static_cast<uint16_t>(value & 0xFFFFU));
return RequestId(static_cast<uint16_t>(value >> 16U), static_cast<uint16_t>(value & 0xFFFFU));
}

bool operator==(const RequestId& other) const {
Expand Down
8 changes: 4 additions & 4 deletions include/transport/udp_transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,10 @@ class UdpTransport : public ITransport {
Result join_multicast_group(const std::string& multicast_address);
Result leave_multicast_group(const std::string& multicast_address);

// Disable copy and assignment
UdpTransport(const UdpTransport&) = delete;
UdpTransport& operator=(const UdpTransport&) = delete;

private:
Endpoint local_endpoint_;
UdpTransportConfig config_;
Expand Down Expand Up @@ -110,10 +114,6 @@ class UdpTransport : public ITransport {
sockaddr_in create_sockaddr(const Endpoint& endpoint) const;
Endpoint sockaddr_to_endpoint(const sockaddr_in& addr) const;
bool is_multicast_address(const std::string& address) const;

// Disable copy and assignment
UdpTransport(const UdpTransport&) = delete;
UdpTransport& operator=(const UdpTransport&) = delete;
};

} // namespace someip::transport
Expand Down
125 changes: 125 additions & 0 deletions scripts/clang_tidy_to_allure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Convert a clang-tidy report into Allure-compatible JUnit XML.

Each clang-tidy check becomes a test suite, each unique warning becomes a
failing test case. The quality-gate result (pass/fail + counts) is emitted
as its own top-level test case.

Usage:
python3 clang_tidy_to_allure.py <report.txt> <output-dir> [--threshold N]

The output directory will contain a single JUnit XML file that the Allure
report workflow can ingest.
"""

import argparse
import re
import sys
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
import xml.etree.ElementTree as ET
from collections import defaultdict
from pathlib import Path


def parse_report(report_path: str) -> tuple[list[tuple[str, int, str, str]], dict[str, int]]:
warnings: list[tuple[str, int, str, str]] = []
summary: dict[str, int] = {}

with open(report_path) as f:
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
for line in f:
m = re.match(
r"(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]", line.strip()
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Harden warning regex to parse the trailing check token reliably.

Lines 29-31 can misparse messages that contain bracketed content inside the diagnostic text. Anchor to full line and capture the final [check] token.

Suggested fix
-            m = re.match(
-                r"(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]", line.strip()
-            )
+            m = re.match(
+                r"^(.*):(\d+):\d+: warning: (.*) \[([^\]]+)\]$",
+                line.rstrip("\n"),
+            )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
m = re.match(
r"(.+?):(\d+):\d+: warning: (.+?) \[(.+?)\]", line.strip()
)
m = re.match(
r"^(.*):(\d+):\d+: warning: (.*) \[([^\]]+)\]$",
line.rstrip("\n"),
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/clang_tidy_to_allure.py` around lines 29 - 31, The current re.match
that assigns m can miscapture bracketed text inside the diagnostic message;
update the regex used in the re.match call so it anchors to the start and end of
the line and explicitly captures the final check token in square brackets (e.g.,
change the pattern to require the trailing `[...]` at the end and use a
character-class like `[^\]]+` for that capture, while allowing the message
capture to span internal brackets), so the match reliably extracts filename,
line, message, and the final check name when called as m = re.match(...,
line.strip()).

if m:
filepath, lineno, message, check = (
m.group(1),
int(m.group(2)),
m.group(3),
m.group(4),
)
warnings.append((filepath, lineno, message, check))
continue

for key in ("Total Warnings", "Total Errors", "Total Issues", "Files with issues"):
if line.strip().startswith(f"{key}:"):
val = line.strip().split(":")[-1].strip()
if val.isdigit():
summary[key] = int(val)

return warnings, summary


def build_xml(
warnings: list[tuple[str, int, str, str]],
summary: dict[str, int],
threshold: int | None,
) -> ET.Element:
testsuites = ET.Element("testsuites")

by_check: dict[str, list[tuple[str, int, str]]] = defaultdict(list)
for filepath, lineno, message, check in warnings:
by_check[check].append((filepath, lineno, message))

for check, items in sorted(by_check.items()):
unique = sorted(set(items))
suite = ET.SubElement(
testsuites,
"testsuite",
name=f"clang-tidy/{check}",
tests=str(len(unique)),
failures=str(len(unique)),
)
for filepath, lineno, message in unique:
tc = ET.SubElement(
suite,
"testcase",
classname=f"clang-tidy.{check}",
name=f"{filepath}:{lineno}",
)
failure = ET.SubElement(tc, "failure", message=message)
failure.text = f"{filepath}:{lineno}: {message} [{check}]"

total = summary.get("Total Issues", len(warnings))
gate_suite = ET.SubElement(
testsuites,
"testsuite",
name="clang-tidy/quality-gate",
tests="1",
failures="0" if (threshold is None or total <= threshold) else "1",
)
tc = ET.SubElement(
gate_suite,
"testcase",
classname="clang-tidy.quality-gate",
name=f"violations={total}" + (f" threshold={threshold}" if threshold else ""),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
)
if threshold is not None and total > threshold:
failure = ET.SubElement(
tc, "failure", message=f"Violation count ({total}) exceeds threshold ({threshold})"
)
failure.text = f"FAILED: {total} > {threshold}"

return testsuites


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("report", help="Path to clang-tidy-report.txt")
parser.add_argument("output_dir", help="Output directory for JUnit XML")
parser.add_argument(
"--threshold", type=int, default=None, help="Quality-gate threshold"
)
args = parser.parse_args()

warnings, summary = parse_report(args.report)
xml_root = build_xml(warnings, summary, args.threshold)

out = Path(args.output_dir)
out.mkdir(parents=True, exist_ok=True)
tree = ET.ElementTree(xml_root)
ET.indent(tree, space=" ")
tree.write(out / "clang-tidy-results.xml", encoding="unicode", xml_declaration=True)
print(f"Wrote {len(warnings)} warnings to {out / 'clang-tidy-results.xml'}")


if __name__ == "__main__":
main()
10 changes: 5 additions & 5 deletions src/e2e/e2e_crc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ static constexpr uint8_t SAE_J1850_INIT = 0xFF;
uint8_t calculate_crc8_sae_j1850(const std::vector<uint8_t>& data) {
uint32_t crc_reg = SAE_J1850_INIT;

for (uint8_t byte : data) {
for (const uint8_t byte : data) {
crc_reg ^= static_cast<uint32_t>(byte);
for (int i = 0; i < 8; ++i) {
if ((crc_reg & 0x80U) != 0) {
Expand All @@ -59,7 +59,7 @@ static constexpr uint16_t ITU_X25_INIT = 0xFFFF;
uint16_t calculate_crc16_itu_x25(const std::vector<uint8_t>& data) {
uint32_t crc_reg = ITU_X25_INIT;

for (uint8_t byte : data) {
for (const uint8_t byte : data) {
crc_reg ^= static_cast<uint32_t>(byte) << 8U;
for (int i = 0; i < 8; ++i) {
if ((crc_reg & 0x8000U) != 0) {
Expand Down Expand Up @@ -105,8 +105,8 @@ uint32_t calculate_crc32(const std::vector<uint8_t>& data) {

uint32_t crc = CRC32_INIT;

for (uint8_t byte : data) {
uint32_t index = ((crc >> 24U) ^ static_cast<uint32_t>(byte)) & 0xFFU;
for (const uint8_t byte : data) {
const uint32_t index = ((crc >> 24U) ^ static_cast<uint32_t>(byte)) & 0xFFU;
crc = (crc << 8U) ^ crc32_table[index];
}

Expand All @@ -120,7 +120,7 @@ std::optional<uint32_t> calculate_crc(const std::vector<uint8_t>& data, size_t o
}

auto first = data.begin() + static_cast<std::ptrdiff_t>(offset);
std::vector<uint8_t> slice(first, first + static_cast<std::ptrdiff_t>(length));
const std::vector<uint8_t> slice(first, first + static_cast<std::ptrdiff_t>(length));

switch (crc_type) {
case 0: // SAE-J1850 (8-bit)
Expand Down
1 change: 0 additions & 1 deletion src/e2e/e2e_profile_registry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

#include "e2e/e2e_profile_registry.h"

#include "e2e/e2e_config.h"
#include "platform/thread.h"

#include <cstdint>
Expand Down
4 changes: 2 additions & 2 deletions src/e2e/e2e_profiles/standard_profile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ class BasicE2EProfile : public E2EProfile {
// which includes E2E header. However, we need to be careful - the actual length
// in the message will be set by update_length() after we set the E2E header.
// For now, calculate what the length will be:
size_t const e2e_size = E2EHeader::get_header_size();
uint32_t length = 8 + e2e_size + static_cast<uint32_t>(msg.get_payload().size());
size_t const e2e_size = E2EHeader::get_header_size();
const uint32_t length = 8 + e2e_size + static_cast<uint32_t>(msg.get_payload().size());
uint32_t length_be = someip_htonl(length);
crc_data.insert(crc_data.end(), reinterpret_cast<const uint8_t*>(&length_be),
reinterpret_cast<const uint8_t*>(&length_be) + sizeof(uint32_t));
Expand Down
7 changes: 6 additions & 1 deletion src/events/event_publisher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@

namespace someip::events {

// NOLINTBEGIN(misc-include-cleaner) - platform::Mutex / platform::this_thread from platform/thread.h (IWYU false positives in impl).

/**
* @brief Event Publisher implementation
* @implements REQ_ARCH_001
Expand Down Expand Up @@ -208,6 +210,7 @@ class EventPublisherImpl : public transport::ITransportListener {
std::vector<uint16_t> get_registered_events() const {
platform::ScopedLock const events_lock(events_mutex_);
std::vector<uint16_t> events;
events.reserve(registered_events_.size());

for (const auto& pair : registered_events_) {
events.push_back(pair.first);
Expand Down Expand Up @@ -295,7 +298,7 @@ class EventPublisherImpl : public transport::ITransportListener {
}
}

for (uint16_t eid : events_to_publish) {
for (const uint16_t eid : events_to_publish) {
publish_event(eid, {});
}
}
Expand Down Expand Up @@ -419,4 +422,6 @@ EventPublisher::Statistics EventPublisher::get_statistics() const {
return impl_->get_statistics();
}

// NOLINTEND(misc-include-cleaner)

} // namespace someip::events
Loading
Loading