Skip to content
1 change: 1 addition & 0 deletions contract-tests/client-contract-tests/src/main.cpp

@tanderson-ld tanderson-ld Jul 22, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Existing lint issues are being ignored by this PR.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#include "server.hpp"

Check failure on line 1 in contract-tests/client-contract-tests/src/main.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

contract-tests/client-contract-tests/src/main.cpp:1:10 [clang-diagnostic-error]

'server.hpp' file not found

#include <launchdarkly/logging/console_backend.hpp>

Expand All @@ -18,7 +18,7 @@
using launchdarkly::LogLevel;

int main(int argc, char* argv[]) {
launchdarkly::Logger logger{

Check warning on line 21 in contract-tests/client-contract-tests/src/main.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

contract-tests/client-contract-tests/src/main.cpp:21:26 [cppcoreguidelines-init-variables]

variable 'logger' is not initialized
std::make_unique<ConsoleBackend>("client-contract-tests")};

std::string const default_port = "8123";
Expand All @@ -31,8 +31,8 @@
try {
net::io_context ioc{1};

auto p = boost::lexical_cast<unsigned short>(port);

Check warning on line 34 in contract-tests/client-contract-tests/src/main.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

contract-tests/client-contract-tests/src/main.cpp:34:14 [readability-identifier-length]

variable name 'p' is too short, expected at least 3 characters
server srv(ioc, "0.0.0.0", p, logger);

Check warning on line 35 in contract-tests/client-contract-tests/src/main.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

contract-tests/client-contract-tests/src/main.cpp:35:16 [cppcoreguidelines-init-variables]

variable 'srv' is not initialized

srv.add_capability("client-side");
srv.add_capability("mobile");
Expand All @@ -47,6 +47,7 @@
srv.add_capability("tls:skip-verify-peer");
srv.add_capability("tls:custom-ca");
srv.add_capability("client-prereq-events");
srv.add_capability("client-prereq-cycle-detection");
srv.add_capability("wrapper");
// Proxies are supported only with CURL networking.
#ifdef LD_CURL_NETWORKING
Expand Down
31 changes: 24 additions & 7 deletions libs/client-sdk/src/client_impl.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#include "client_impl.hpp"
#include "data_sources/null_data_source.hpp"
#include "data_sources/polling_data_source.hpp"
Expand Down Expand Up @@ -211,7 +211,7 @@
std::unordered_map<Client::FlagKey, Value> result;
for (auto& [key, descriptor] : flag_manager_.Store().GetAll()) {
if (descriptor->item) {
result.try_emplace(key, descriptor->item->Detail().Value());

Check warning on line 214 in libs/client-sdk/src/client_impl.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.cpp:214:37 [bugprone-unchecked-optional-access]

unchecked access to optional value
}
}
return result;
Expand Down Expand Up @@ -244,10 +244,11 @@
}

template <typename T>
EvaluationDetail<T> ClientImpl::VariationInternal(FlagKey const& key,

Check warning on line 247 in libs/client-sdk/src/client_impl.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.cpp:247:51 [bugprone-easily-swappable-parameters]

2 adjacent parameters of 'VariationInternal' of convertible types are easily swapped by mistake

Check warning on line 247 in libs/client-sdk/src/client_impl.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.cpp:247:33 [readability-function-cognitive-complexity]

function 'VariationInternal' has cognitive complexity of 27 (threshold 25)
Value default_value,
bool check_type,
bool detailed) {
bool detailed,
std::unordered_set<std::string>* visited) {
auto desc = flag_manager_.Store().Get(key);

events::FeatureEventParams event = {
Expand Down Expand Up @@ -300,7 +301,7 @@

LD_ASSERT(desc->item);

auto const& flag = *(desc->item);

Check warning on line 304 in libs/client-sdk/src/client_impl.cpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.cpp:304:25 [bugprone-unchecked-optional-access]

unchecked access to optional value
auto const& detail = flag.Detail();

// The Prerequisites vector represents the evaluated prerequisites of
Expand All @@ -308,19 +309,35 @@
// prerequisites (recursively), which is necessary to ensure LaunchDarkly
// analytics functions properly.
//
// We're using JsonVariation because the type of the
// prerequisite is both unknown and irrelevant to emitting the events.
//
// We're passing Value::Null() to match a server-side SDK's behavior when

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.

The part of this comment about Value::Null() is still valid, right? Maybe we should keep that part?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good catch — I over-trimmed. Restored the Value::Null() sentence in b9e27d5d. Dropped only the sibling "we're using JsonVariation" sentence since we now call VariationInternal<Value> directly, so that part is stale.

// evaluating prerequisites.
//
// `visited` tracks the current path so a cyclic prerequisite graph
// terminates instead of recursing without bound.
//
// NOTE: if "hooks" functionality is implemented into this SDK, take care
// that evaluating prerequisites does not trigger hooks. This may require
// refactoring the code below to not use JsonVariation.
if (auto const prereqs = flag.Prerequisites()) {
// refactoring the code below.
auto const prereqs = flag.Prerequisites();
if (prereqs && !prereqs->empty()) {
std::unordered_set<std::string> local_visited;
auto* ancestors = visited ? visited : &local_visited;
ancestors->insert(key);
for (auto const& prereq : *prereqs) {
JsonVariation(prereq, Value::Null());
if (ancestors->count(prereq) != 0) {
// Cyclic edge: skip descent, continue with remaining
// prerequisites at this level. The requested flag's value
// and reason (below) are unaffected.
LD_LOG(logger_, LogLevel::kWarn)
<< "prerequisite cycle detected: skipping edge from '"
<< key << "' to '" << prereq << "'";
continue;
}
(void)VariationInternal<Value>(prereq, Value::Null(),
/*check_type=*/false,
/*detailed=*/false, ancestors);
}
ancestors->erase(key);
}

if (check_type && default_value.Type() != Value::Type::kNull &&
Expand Down
5 changes: 4 additions & 1 deletion libs/client-sdk/src/client_impl.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#pragma once

#include "data_sources/data_source.hpp"
Expand All @@ -23,7 +23,9 @@
#include <memory>
#include <optional>
#include <shared_mutex>
#include <string>
#include <thread>
#include <unordered_set>

namespace launchdarkly::client_side {
class ClientImpl : public IClient {
Expand Down Expand Up @@ -84,7 +86,7 @@

flag_manager::IFlagNotifier& FlagNotifier() override;

~ClientImpl();

Check warning on line 89 in libs/client-sdk/src/client_impl.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.hpp:89:5 [cppcoreguidelines-explicit-virtual-functions]

annotate this function with 'override' or (rarely) 'final'

std::future<bool> StartAsync() override;

Expand All @@ -93,13 +95,14 @@
[[nodiscard]] EvaluationDetail<T> VariationInternal(FlagKey const& key,
Value default_value,
bool check_type,
bool detailed);
bool detailed,
std::unordered_set<std::string>* visited = nullptr);
void TrackInternal(std::string event_name,
std::optional<Value> data,
std::optional<double> metric_value);

template <typename F>
auto ReadContextSynchronized(F fn) const

Check warning on line 105 in libs/client-sdk/src/client_impl.hpp

View workflow job for this annotation

GitHub Actions / cpp-linter

libs/client-sdk/src/client_impl.hpp:105:36 [readability-identifier-length]

parameter name 'fn' is too short, expected at least 3 characters
-> std::invoke_result_t<F, Context const&> {
std::shared_lock lock(context_mutex_);
return fn(context_);
Expand Down
Loading