Skip to content

Enable clang-tidy's readability-function-cognitive-complexity check #7358

Description

Diff to enable this looks something like this:

diff --git a/.clang-tidy b/.clang-tidy
index c0a984def..941f67e03 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -39,11 +39,16 @@ Checks: >
   -performance-no-int-to-ptr,
   portability-*,
   readability-*,
-  -readability-function-cognitive-complexity,
   -readability-identifier-length,
   -readability-avoid-nested-conditional-operator,
   -readability-convert-member-functions-to-static,
 
+CheckOptions:
+  - key: readability-function-cognitive-complexity.IgnoreMacros
+    value: 'true'
+  - key: readability-function-cognitive-complexity.Threshold
+    value: '50'
+
 WarningsAsErrors: '*'
 HeaderFilterRegex: '?!(3rdparty)'
 FormatStyle:     'file'

I think we definitely want to ignore macros, because our logging macros are measured as extremely complex but in practice don't make the functions harder to read.

The default threshold is 25, which flags a huge number of functions, and I think is a little low. I suggest we start with a higher threshold, such as 50, for at least an initial pass.

A benefit of this (beyond pure readability) should be that we improve the clang-tidy coverage for other checks - such as bugprone-unchecked-optional-access - to silently fail, missing clear errors. By simplifying functions, reducing the scope that these checks need to analyse, we should get better coverage. One frustrating niggle is that it's not clear to me what a "safe" threshold for this is - the "cognitive complexity" does not directly correspond with the flow-analysis complexity that causes these checks to fail, and we don't know what threshold the checks fail at. But these measures are likely correlated, and we can do some work to validate where certain checks are and are not being run.

Remaining work after #8504

The check is already enabled by #7995 with Threshold=50 and IgnoreMacros=true. The remaining work is to remove the baseline suppressions through behaviour-preserving refactors.

Inventory checked against main at a6fe2f2 on 2026-10-05, excluding NodeEndpoints::init_handlers, which the now-merged #8504 addresses. The post-#8504 backlog contains six top-level functions plus two nested callbacks, covered by seven suppression sites across five files. The two mechanical follow-up PRs below are open, not yet merged.

Scores below are the historical measurements at bc7f0cf from the earlier comment, not newly measured scores on the current revision. Each refactor should verify that the registration function and all extracted callbacks meet the threshold, rather than merely moving the suppression.

Baseline score Function Location Remaining approach Follow-up
277 Logging sample init_handlers samples/apps/logging/logging.cpp:593 Mechanical: extract inline endpoint handlers, preserving registration, authentication, forwarding, and handler behaviour. #8508 (open)
66 setup_basic_hooks src/node/node_state.h:3277 Mechanical: extract hook callbacks, preserving hook order, capture lifetimes, and commit/rollback behaviour. #8509 (open)
125; nested callbacks 89 and 61 initiate_join_unsafe and its response/task callbacks src/node/node_state.h:1382 Partly mechanical: callback extraction reduces the outer score, but the callbacks themselves still need decomposition of join-response handling. Not started
69 fill_range_response_from_file src/node/rpc/file_serving_handlers.h:173 Control-flow decomposition: isolate range parsing/response construction while preserving validation and HTTP semantics. Not started
90 process_command_inner src/node/rpc/frontend.h:707 Careful request-path decomposition: preserve authentication, forwarding, transaction/retry behaviour, and error paths. Not started
102 do_execute_request src/js/registry.cpp:58 Careful JS execution-path decomposition: preserve exception/timeout handling, ownership, and response semantics. Not started

Already addressed since the earlier inventory: the two unit-test suppressions (#8482), governance ack handlers (#8484), service-state handlers (#8486), proposal handlers (#8487), file-serving handler registration (#8488), and the programmability constructor (#8489). Node endpoint registration is addressed by #8504.

Actual clang-tidy 18.1.8 measurements with IgnoreMacros=true in the open follow-ups: #8508 reduces logging init_handlers from 277 to 0, with all 41 extracted handlers scoring at most 31; #8509 reduces setup_basic_hooks from 66 to 0, with all six extracted callbacks scoring at most 12. All retained registration adapters in both PRs score 0. Neither PR changes thresholds or adds complexity suppressions.

After these two PRs land, the three control-flow refactors and the partially mechanical join-response item remain for assessment.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions