From 40d436746c8e0fb633e5bea51046d9a241ff7fcc Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 10:28:10 -1000 Subject: [PATCH 1/8] webserver needs as well --- esphome/components/web_server/web_server.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index b88615ce47..61e749e059 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1322,6 +1322,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); From 13ceda899b95b47110da15c9fa625c92f02a5ba1 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 11:00:25 -1000 Subject: [PATCH 2/8] add some more , rearrange --- esphome/components/mqtt/mqtt_client.cpp | 2 +- esphome/components/mqtt/mqtt_component.cpp | 2 +- esphome/components/web_server/web_server.cpp | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 804cb3be31..3bd485a386 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -92,10 +92,10 @@ void MQTTClientComponent::send_device_info_() { std::string topic = "esphome/discover/"; topic.append(App.get_name()); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson this->publish_json( topic, [](JsonObject root) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson uint8_t index = 0; for (auto &ip : network::get_ip_addresses()) { if (ip.is_set()) { diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 5f00bfddf6..9eb8d6175d 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -70,10 +70,10 @@ bool MQTTComponent::send_discovery_() { ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), [this](JsonObject root) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson SendDiscoveryConfig config; config.state_topic = true; config.command_topic = true; diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 61e749e059..01643858ba 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1683,6 +1683,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; From d6e05061f88ec1f7f430a9265b42553dc1b1918c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 11:19:49 -1000 Subject: [PATCH 3/8] a few more --- esphome/components/mqtt/mqtt_button.cpp | 5 +++-- esphome/components/mqtt/mqtt_client.cpp | 1 + esphome/components/mqtt/mqtt_climate.cpp | 2 +- esphome/components/mqtt/mqtt_component.cpp | 1 + esphome/components/mqtt/mqtt_light.cpp | 1 + esphome/components/mqtt/mqtt_number.cpp | 2 +- esphome/components/mqtt/mqtt_select.cpp | 2 +- esphome/components/web_server/web_server.cpp | 8 ++++++++ 8 files changed, 17 insertions(+), 5 deletions(-) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index e9f81dafcd..bf96f2343b 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -31,10 +31,11 @@ void MQTTButtonComponent::dump_config() { } void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; - if (!this->button_->get_device_class().empty()) + if (!this->button_->get_device_class().empty()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); + } } std::string MQTTButtonComponent::component_type() const { return "button"; } diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 3bd485a386..9ab903a2d9 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -103,6 +103,7 @@ void MQTTClientComponent::send_device_info_() { index++; } } + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["name"] = App.get_name(); if (!App.get_friendly_name().empty()) { root["friendly_name"] = App.get_friendly_name(); diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 1eeb01ee05..4477e6afe3 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -14,10 +14,10 @@ static const char *const TAG = "mqtt.climate"; using namespace esphome::climate; void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto traits = this->device_->get_traits(); // current_temperature_topic if (traits.get_supports_current_temperature()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_CURRENT_TEMPERATURE_TOPIC] = this->get_current_temperature_state_topic(); } // current_humidity_topic diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index 9eb8d6175d..adb24517a7 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -81,6 +81,7 @@ bool MQTTComponent::send_discovery_() { this->send_discovery(root, config); // Set subscription QoS (default is 0) if (this->subscribe_qos_ != 0) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_QOS] = this->subscribe_qos_; } diff --git a/esphome/components/mqtt/mqtt_light.cpp b/esphome/components/mqtt/mqtt_light.cpp index 0bb0ea661a..4f5ff408a4 100644 --- a/esphome/components/mqtt/mqtt_light.cpp +++ b/esphome/components/mqtt/mqtt_light.cpp @@ -45,6 +45,7 @@ void MQTTJSONLightComponent::send_discovery(JsonObject root, mqtt::SendDiscovery auto traits = this->state_->get_traits(); root[MQTT_COLOR_MODE] = true; + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray color_modes = root["supported_color_modes"].to(); if (traits.supports_color_mode(ColorMode::ON_OFF)) color_modes.add("onoff"); diff --git a/esphome/components/mqtt/mqtt_number.cpp b/esphome/components/mqtt/mqtt_number.cpp index 1217dd56a7..a44632ff30 100644 --- a/esphome/components/mqtt/mqtt_number.cpp +++ b/esphome/components/mqtt/mqtt_number.cpp @@ -38,9 +38,9 @@ std::string MQTTNumberComponent::component_type() const { return "number"; } const EntityBase *MQTTNumberComponent::get_entity() const { return this->number_; } void MQTTNumberComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson const auto &traits = number_->traits; // https://www.home-assistant.io/integrations/number.mqtt/ + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_MIN] = traits.get_min_value(); root[MQTT_MAX] = traits.get_max_value(); root[MQTT_STEP] = traits.get_step(); diff --git a/esphome/components/mqtt/mqtt_select.cpp b/esphome/components/mqtt/mqtt_select.cpp index c1c958b9e7..b851348306 100644 --- a/esphome/components/mqtt/mqtt_select.cpp +++ b/esphome/components/mqtt/mqtt_select.cpp @@ -33,9 +33,9 @@ std::string MQTTSelectComponent::component_type() const { return "select"; } const EntityBase *MQTTSelectComponent::get_entity() const { return this->select_; } void MQTTSelectComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson const auto &traits = select_->traits; // https://www.home-assistant.io/integrations/select.mqtt/ + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray options = root[MQTT_OPTIONS].to(); for (const auto &option : traits.get_options()) options.add(option); diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 01643858ba..1579ccd730 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1324,6 +1324,7 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); @@ -1331,31 +1332,37 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf char buf[16]; if (start_config == DETAIL_ALL) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["modes"].to(); for (climate::ClimateMode m : traits.get_supported_modes()) opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); if (!traits.get_supported_custom_fan_modes().empty()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["fan_modes"].to(); for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["custom_fan_modes"].to(); for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) opt.add(custom_fan_mode); } if (traits.get_supports_swing_modes()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["swing_modes"].to(); for (auto swing_mode : traits.get_supported_swing_modes()) opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode))); } if (traits.get_supports_presets() && obj->preset.has_value()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["presets"].to(); for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["custom_presets"].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); @@ -1685,6 +1692,7 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { + // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; switch (obj->state) { From 1778776b7369f2bae3a201deaad6068ce0672c6b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 11:30:43 -1000 Subject: [PATCH 4/8] use NOLINTBEGIN/NOLINTEND for the multi occ cases --- esphome/components/json/json_util.cpp | 11 ++++------- esphome/components/mqtt/mqtt_client.cpp | 4 ++-- esphome/components/mqtt/mqtt_component.cpp | 5 ++--- esphome/components/web_server/web_server.cpp | 14 ++++---------- 4 files changed, 12 insertions(+), 22 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 29e2a65bbd..1ff5105137 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -26,18 +26,15 @@ struct SpiRamAllocator : ArduinoJson::Allocator { }; std::string build_json(const json_build_t &f) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto doc_allocator = SpiRamAllocator(); JsonDocument json_document(&doc_allocator); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (json_document.overflowed()) { ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); return "{}"; } - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonObject root = json_document.to(); f(root); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (json_document.overflowed()) { ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); return "{}"; @@ -45,18 +42,17 @@ std::string build_json(const json_build_t &f) { std::string output; serializeJson(json_document, output); return output; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } bool parse_json(const std::string &data, const json_parse_t &f) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto doc_allocator = SpiRamAllocator(); JsonDocument json_document(&doc_allocator); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson if (json_document.overflowed()) { ESP_LOGE(TAG, "Could not allocate memory for JSON document!"); return false; } - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson DeserializationError err = deserializeJson(json_document, data); JsonObject root = json_document.as(); @@ -69,6 +65,7 @@ bool parse_json(const std::string &data, const json_parse_t &f) { } ESP_LOGE(TAG, "Parse error: %s", err.c_str()); return false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } } // namespace json diff --git a/esphome/components/mqtt/mqtt_client.cpp b/esphome/components/mqtt/mqtt_client.cpp index 9ab903a2d9..5b93789447 100644 --- a/esphome/components/mqtt/mqtt_client.cpp +++ b/esphome/components/mqtt/mqtt_client.cpp @@ -92,7 +92,7 @@ void MQTTClientComponent::send_device_info_() { std::string topic = "esphome/discover/"; topic.append(App.get_name()); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson this->publish_json( topic, [](JsonObject root) { @@ -103,7 +103,6 @@ void MQTTClientComponent::send_device_info_() { index++; } } - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root["name"] = App.get_name(); if (!App.get_friendly_name().empty()) { root["friendly_name"] = App.get_friendly_name(); @@ -149,6 +148,7 @@ void MQTTClientComponent::send_device_info_() { #endif }, 2, this->discovery_info_.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClientComponent::dump_config() { diff --git a/esphome/components/mqtt/mqtt_component.cpp b/esphome/components/mqtt/mqtt_component.cpp index adb24517a7..b51f4d903e 100644 --- a/esphome/components/mqtt/mqtt_component.cpp +++ b/esphome/components/mqtt/mqtt_component.cpp @@ -70,7 +70,7 @@ bool MQTTComponent::send_discovery_() { ESP_LOGV(TAG, "'%s': Sending discovery", this->friendly_name().c_str()); - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return global_mqtt_client->publish_json( this->get_discovery_topic_(discovery_info), [this](JsonObject root) { @@ -81,7 +81,6 @@ bool MQTTComponent::send_discovery_() { this->send_discovery(root, config); // Set subscription QoS (default is 0) if (this->subscribe_qos_ != 0) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_QOS] = this->subscribe_qos_; } @@ -190,11 +189,11 @@ bool MQTTComponent::send_discovery_() { device_info[MQTT_DEVICE_SUGGESTED_AREA] = node_area; } - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson device_info[MQTT_DEVICE_CONNECTIONS][0][0] = "mac"; device_info[MQTT_DEVICE_CONNECTIONS][0][1] = mac; }, this->qos_, discovery_info.retain); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } uint8_t MQTTComponent::get_qos() const { return this->qos_; } diff --git a/esphome/components/web_server/web_server.cpp b/esphome/components/web_server/web_server.cpp index 1579ccd730..9ec667dbc5 100644 --- a/esphome/components/web_server/web_server.cpp +++ b/esphome/components/web_server/web_server.cpp @@ -1322,9 +1322,8 @@ std::string WebServer::climate_all_json_generator(WebServer *web_server, void *s return web_server->climate_json((climate::Climate *) (source), DETAIL_ALL); } std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson set_json_id(root, obj, "climate-" + obj->get_object_id(), start_config); const auto traits = obj->get_traits(); int8_t target_accuracy = traits.get_target_temperature_accuracy_decimals(); @@ -1332,37 +1331,31 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf char buf[16]; if (start_config == DETAIL_ALL) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["modes"].to(); for (climate::ClimateMode m : traits.get_supported_modes()) opt.add(PSTR_LOCAL(climate::climate_mode_to_string(m))); if (!traits.get_supported_custom_fan_modes().empty()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["fan_modes"].to(); for (climate::ClimateFanMode m : traits.get_supported_fan_modes()) opt.add(PSTR_LOCAL(climate::climate_fan_mode_to_string(m))); } if (!traits.get_supported_custom_fan_modes().empty()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["custom_fan_modes"].to(); for (auto const &custom_fan_mode : traits.get_supported_custom_fan_modes()) opt.add(custom_fan_mode); } if (traits.get_supports_swing_modes()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["swing_modes"].to(); for (auto swing_mode : traits.get_supported_swing_modes()) opt.add(PSTR_LOCAL(climate::climate_swing_mode_to_string(swing_mode))); } if (traits.get_supports_presets() && obj->preset.has_value()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["presets"].to(); for (climate::ClimatePreset m : traits.get_supported_presets()) opt.add(PSTR_LOCAL(climate::climate_preset_to_string(m))); } if (!traits.get_supported_custom_presets().empty() && obj->custom_preset.has_value()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray opt = root["custom_presets"].to(); for (auto const &custom_preset : traits.get_supported_custom_presets()) opt.add(custom_preset); @@ -1415,6 +1408,7 @@ std::string WebServer::climate_json(climate::Climate *obj, JsonDetail start_conf root["state"] = root["target_temperature"]; } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif @@ -1690,9 +1684,8 @@ std::string WebServer::update_all_json_generator(WebServer *web_server, void *so return web_server->update_json((update::UpdateEntity *) (source), DETAIL_STATE); } std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_config) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson return json::build_json([this, obj, start_config](JsonObject root) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson set_json_id(root, obj, "update-" + obj->get_object_id(), start_config); root["value"] = obj->update_info.latest_version; switch (obj->state) { @@ -1717,6 +1710,7 @@ std::string WebServer::update_json(update::UpdateEntity *obj, JsonDetail start_c this->add_sorting_info_(root, obj); } }); + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } #endif From a714e8da0b75bdab2b54e0eeff89617882d76147 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 11:52:11 -1000 Subject: [PATCH 5/8] last ones --- esphome/components/mqtt/mqtt_button.cpp | 3 ++- esphome/components/mqtt/mqtt_climate.cpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/mqtt/mqtt_button.cpp b/esphome/components/mqtt/mqtt_button.cpp index bf96f2343b..c619a02344 100644 --- a/esphome/components/mqtt/mqtt_button.cpp +++ b/esphome/components/mqtt/mqtt_button.cpp @@ -31,11 +31,12 @@ void MQTTButtonComponent::dump_config() { } void MQTTButtonComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson config.state_topic = false; if (!this->button_->get_device_class().empty()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_DEVICE_CLASS] = this->button_->get_device_class(); } + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } std::string MQTTButtonComponent::component_type() const { return "button"; } diff --git a/esphome/components/mqtt/mqtt_climate.cpp b/esphome/components/mqtt/mqtt_climate.cpp index 4477e6afe3..e16f097812 100644 --- a/esphome/components/mqtt/mqtt_climate.cpp +++ b/esphome/components/mqtt/mqtt_climate.cpp @@ -14,10 +14,10 @@ static const char *const TAG = "mqtt.climate"; using namespace esphome::climate; void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryConfig &config) { + // NOLINTBEGIN(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson auto traits = this->device_->get_traits(); // current_temperature_topic if (traits.get_supports_current_temperature()) { - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson root[MQTT_CURRENT_TEMPERATURE_TOPIC] = this->get_current_temperature_state_topic(); } // current_humidity_topic @@ -29,7 +29,6 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo // mode_state_topic root[MQTT_MODE_STATE_TOPIC] = this->get_mode_state_topic(); // modes - // NOLINTNEXTLINE(clang-analyzer-cplusplus.NewDeleteLeaks) false positive with ArduinoJson JsonArray modes = root[MQTT_MODES].to(); // sort array for nice UI in HA if (traits.supports_mode(CLIMATE_MODE_AUTO)) @@ -165,6 +164,7 @@ void MQTTClimateComponent::send_discovery(JsonObject root, mqtt::SendDiscoveryCo config.state_topic = false; config.command_topic = false; + // NOLINTEND(clang-analyzer-cplusplus.NewDeleteLeaks) } void MQTTClimateComponent::setup() { auto traits = this->device_->get_traits(); From 52d680161871d007e1c6c8575a452b765fe24b8b Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 12:29:40 -1000 Subject: [PATCH 6/8] address bot comments --- .../http_request/update/http_request_update.cpp | 3 +-- esphome/components/json/json_util.cpp | 8 ++++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/esphome/components/http_request/update/http_request_update.cpp b/esphome/components/http_request/update/http_request_update.cpp index 5e6e7b75d9..06aa6da6a4 100644 --- a/esphome/components/http_request/update/http_request_update.cpp +++ b/esphome/components/http_request/update/http_request_update.cpp @@ -83,8 +83,7 @@ void HttpRequestUpdate::update_task(void *params) { container.reset(); // Release ownership of the container's shared_ptr valid = json::parse_json(response, [this_update](JsonObject root) -> bool { - if (!root["name"].is() || !root["version"].is() || - !root["builds"].is()) { + if (!root["name"].is() || !root["version"].is() || !root["builds"].is()) { ESP_LOGE(TAG, "Manifest does not contain required fields"); return false; } diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 1ff5105137..5f47444582 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -1,7 +1,7 @@ #include "json_util.h" #include "esphome/core/log.h" -#include +// ArduinoJson::Allocator is included via ArduinoJson.h in json_util.h namespace esphome { namespace json { @@ -13,7 +13,11 @@ struct SpiRamAllocator : ArduinoJson::Allocator { void *allocate(size_t size) override { return this->allocator_.allocate(size); } void deallocate(void *pointer) override { - // RAMAllocator requires passing the size of the allocated space which don't know, so use free directly + // ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate. + // RAMAllocator::deallocate() requires the size, which we don't have access to here. + // Since RAMAllocator internally uses malloc/free for NONE mode (PSRAM allocation), + // it's safe to use free() directly. The memory was allocated via malloc in + // RAMAllocator::allocate() when using NONE mode. free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) } From 3cca7a61614368785aac4f92903f39d0d75d8f34 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Jul 2025 12:31:41 -1000 Subject: [PATCH 7/8] fix incorrect comment --- esphome/components/json/json_util.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/esphome/components/json/json_util.cpp b/esphome/components/json/json_util.cpp index 5f47444582..94c531222a 100644 --- a/esphome/components/json/json_util.cpp +++ b/esphome/components/json/json_util.cpp @@ -15,9 +15,10 @@ struct SpiRamAllocator : ArduinoJson::Allocator { void deallocate(void *pointer) override { // ArduinoJson's Allocator interface doesn't provide the size parameter in deallocate. // RAMAllocator::deallocate() requires the size, which we don't have access to here. - // Since RAMAllocator internally uses malloc/free for NONE mode (PSRAM allocation), - // it's safe to use free() directly. The memory was allocated via malloc in - // RAMAllocator::allocate() when using NONE mode. + // RAMAllocator::deallocate implementation just calls free() regardless of whether + // the memory was allocated with heap_caps_malloc or malloc. + // This is safe because ESP-IDF's heap implementation internally tracks the memory region + // and routes free() to the appropriate heap. free(pointer); // NOLINT(cppcoreguidelines-owning-memory,cppcoreguidelines-no-malloc) } From d268c14f7ef69bcee455a60fecea4d2af9f105e7 Mon Sep 17 00:00:00 2001 From: Kevin Ahrendt Date: Mon, 14 Jul 2025 20:20:46 -0400 Subject: [PATCH 8/8] Apply suggestions from code review Fixes unsigned integer wrong type Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com> --- esphome/components/mqtt/mqtt_date.cpp | 2 +- esphome/components/mqtt/mqtt_datetime.cpp | 6 +++--- esphome/components/mqtt/mqtt_time.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/esphome/components/mqtt/mqtt_date.cpp b/esphome/components/mqtt/mqtt_date.cpp index 5f6923fc3e..0f0a334ae7 100644 --- a/esphome/components/mqtt/mqtt_date.cpp +++ b/esphome/components/mqtt/mqtt_date.cpp @@ -26,7 +26,7 @@ void MQTTDateComponent::setup() { if (root["month"].is()) { call.set_month(root["month"]); } - if (root["day"].is()) { + if (root["day"].is()) { call.set_day(root["day"]); } call.perform(); diff --git a/esphome/components/mqtt/mqtt_datetime.cpp b/esphome/components/mqtt/mqtt_datetime.cpp index c74cb159c8..5c56baabe0 100644 --- a/esphome/components/mqtt/mqtt_datetime.cpp +++ b/esphome/components/mqtt/mqtt_datetime.cpp @@ -29,13 +29,13 @@ void MQTTDateTimeComponent::setup() { if (root["day"].is()) { call.set_day(root["day"]); } - if (root["hour"].is()) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root["minute"].is()) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root["second"].is()) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform(); diff --git a/esphome/components/mqtt/mqtt_time.cpp b/esphome/components/mqtt/mqtt_time.cpp index a19c16bb21..0c95bd8147 100644 --- a/esphome/components/mqtt/mqtt_time.cpp +++ b/esphome/components/mqtt/mqtt_time.cpp @@ -20,13 +20,13 @@ MQTTTimeComponent::MQTTTimeComponent(TimeEntity *time) : time_(time) {} void MQTTTimeComponent::setup() { this->subscribe_json(this->get_command_topic_(), [this](const std::string &topic, JsonObject root) { auto call = this->time_->make_call(); - if (root["hour"].is()) { + if (root["hour"].is()) { call.set_hour(root["hour"]); } - if (root["minute"].is()) { + if (root["minute"].is()) { call.set_minute(root["minute"]); } - if (root["second"].is()) { + if (root["second"].is()) { call.set_second(root["second"]); } call.perform();