diff --git a/esphome/components/api/proto.h b/esphome/components/api/proto.h index 7199e009f4..00ea587f9b 100644 --- a/esphome/components/api/proto.h +++ b/esphome/components/api/proto.h @@ -300,7 +300,7 @@ class ProtoWriteBuffer { constexpr uint32_t VARINT_MAX_1_BYTE = 1 << 7; // 128 constexpr uint32_t VARINT_MAX_2_BYTE = 1 << 14; // 16384 -/// Static encode helpers for generated encode() functions. Each takes the write cursor by value and +/// Static encode helpers for the generated encode bodies. Each takes the write cursor by value and /// returns it advanced, so outlined calls at -Os chain through the return register instead of a /// stack slot. Helpers without a _force suffix skip fields holding the proto3 default. class ProtoEncode { @@ -318,8 +318,8 @@ class ProtoEncode { *pos++ = static_cast(value); return pos; } - static inline uint8_t *ESPHOME_ALWAYS_INLINE encode_varint_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, - uint32_t value) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + encode_varint_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t value) { if (value < VARINT_MAX_1_BYTE) [[likely]] { PROTO_ENCODE_CHECK_BOUNDS(pos, 1); *pos++ = static_cast(value); @@ -343,8 +343,8 @@ class ProtoEncode { } return encode_varint_raw_loop(pos PROTO_ENCODE_DEBUG_ARG, value); } - static inline uint8_t *ESPHOME_ALWAYS_INLINE encode_varint_raw_64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, - uint64_t value) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + encode_varint_raw_64(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint64_t value) { if (value < VARINT_MAX_1_BYTE) [[likely]] { PROTO_ENCODE_CHECK_BOUNDS(pos, 1); *pos++ = static_cast(value); @@ -377,26 +377,27 @@ class ProtoEncode { } return encode_varint_raw_64(pos PROTO_ENCODE_DEBUG_ARG, value); } - static inline uint8_t *ESPHOME_ALWAYS_INLINE encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, - uint32_t field_id, uint32_t type) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + encode_field_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint32_t field_id, uint32_t type) { return encode_varint_raw(pos PROTO_ENCODE_DEBUG_ARG, (field_id << 3) | type); } /// Write a single precomputed tag byte. Tag must be < 128. - static inline uint8_t *ESPHOME_ALWAYS_INLINE write_raw_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, - uint8_t b) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + write_raw_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, uint8_t b) { PROTO_ENCODE_CHECK_BOUNDS(pos, 1); *pos++ = b; return pos; } /// Reserve one byte for later backpatch (e.g., sub-message length). /// Advances pos past the reserved byte without writing a value. - static inline uint8_t *ESPHOME_ALWAYS_INLINE reserve_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + reserve_byte(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM) { PROTO_ENCODE_CHECK_BOUNDS(pos, 1); return pos + 1; } /// Write raw bytes to the buffer (no tag, no length prefix). - static inline uint8_t *ESPHOME_ALWAYS_INLINE encode_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, - const void *data, size_t len) { + [[nodiscard]] static inline uint8_t *ESPHOME_ALWAYS_INLINE + encode_raw(uint8_t *__restrict__ pos PROTO_ENCODE_DEBUG_PARAM, const void *data, size_t len) { PROTO_ENCODE_CHECK_BOUNDS(pos, len); std::memcpy(pos, data, len); return pos + len; diff --git a/tests/unit_tests/components/api/test_api_protobuf_generator.py b/tests/unit_tests/components/api/test_api_protobuf_generator.py index b872e300ba..c182520c5a 100644 --- a/tests/unit_tests/components/api/test_api_protobuf_generator.py +++ b/tests/unit_tests/components/api/test_api_protobuf_generator.py @@ -155,21 +155,6 @@ SCALAR_TYPES = [ ] -@pytest.mark.parametrize("field_type", SCALAR_TYPES) -@pytest.mark.parametrize("force", [False, True]) -@pytest.mark.parametrize("repeated", [False, True]) -def test_encode_statements_assign_the_returned_cursor( - field_type: int, force: bool, repeated: bool -) -> None: - """Every ProtoEncode call must take pos by value and store the returned cursor.""" - content = _encode_field(field_type, force=force, repeated=repeated) - calls = [line.strip() for line in content.splitlines() if "ProtoEncode::" in line] - assert calls, content - for call in calls: - assert call.startswith("pos = ProtoEncode::"), call - assert ", true)" not in content, content - - @pytest.mark.parametrize("field_type", SCALAR_TYPES) def test_forced_fields_use_the_force_overload_or_raw_writes(field_type: int) -> None: content = _encode_field(field_type, force=True)