#432 cognitive complexity#440
Conversation
… for multiple languages with tests for each - Implemented cognitive complexity tests for Python, R, Ruby, Rust, Scala, Solidity, Structured Text, Swift, TTCN-3, TypeScript, Vue.js, Zig and more. - Each language includes tests for simple functions, single if statements, nested loops, binary logical operators, and other control structures. - Updated error messages in option parsing to include cognitive complexity as a valid sorting factor.
- Implemented a new extension for calculating Cognitive Complexity in functions, following the specification by SonarSource. - Updated output formats to include Cognitive Complexity in headers and outputs for different formats (CSV, HTML). - Adjusted error messages for sorting factors to reflect the new order of fields, ensuring clarity in available options.
…uages, and made it no longer default - Removed cognitive complexity tests from Python, R, Ruby, Rust, Scala, Solidity, Structured Text, Swift, TTCN-3, TypeScript, Vue.js, and Zig test files. - Updated test cases to ensure they accurately reflect the cognitive complexity calculations for various control structures. - Adjusted the expected cognitive complexity values based on the latest implementation details. - Modified the test options to reflect changes in the default and extension fields for cognitive complexity.
|
@terryyin are there any changes you would like to me to make, or scrap the whole PR? |
|
@Ollec sorry, I keep postponing this because too many things are changed. I will review as soon as I can. |
|
@terryyin No worries, I understand. It ended up being a lot more of an undertaking than I had been planning. |
- Removed extra blank line in lizard.py - Fixed mock workaround — replaced _mock_name check with isinstance(index, int) in lizard.py - Removed dead _state_after_name method from erlang.py - Removed duplicate enter_lambda/exit_lambda from core lizard.py (extension's version in CogCFileInfoAddition is kept, which adds pending_lambda_nesting) - Fixed from mock import Mock → from unittest.mock import Mock in test_output_with_cogc.py - Fixed from mock import Mock → from unittest.mock import Mock in test_output_with_cogc.py - Moved cognitive_complexity/initial_nesting_level out of FunctionInfo.__init__ in core — now only initialized by the extension's _init_cogc_data monkey-patch - Moved lambda_depth/has_top_level_increment out of FileInfoBuilder.__init__ in core — now only initialized by the extension's _init_cogc_context_data - Replaced _create_initial_state dict + 18-variable destructuring with direct local variable initialization in __call__, and used lang_names consistently - Updated vague JavaScript test skip reasons with specific limitation descriptions
|
After merging the conflict, these problem still exists. They changed the original behavior even when not using the new extension. C++ raw string pattern removed — clike.py C++ preprocessor tokens now yielded — clike.py C++ lambda parsing simplified — clike.py PL/SQL BEGIN→{ END→} mapping — plsql.py Python bracket_depth + comprehension nesting — python.py TypeScript nested functions as lambdas — typescript.py Python decorator / nested function handling — python.py Structured Text END handling — st.py Erlang reserved keywords / function detection — erlang.py |
|
Thanks for the feedback, I will take a look at this in more depth over the weekend! |
|
@terryyin Did you have any project that you were using to see these specific behaviors? |
…ot active
- Gate C++ preprocessor token yield on _cogc_active flag to prevent
token count and NLOC inflation from leaked #if/#ifdef/#elif tokens
- Gate PLSQL synthetic { and }nosync emission on _cogc_active; restore
master's END_IF/END_LOOP merged tokens when CogC is not processing
- Revert Erlang to master's push-then-fail function detection to
preserve token counting behavior (reserved_keywords was unnecessary)
- Add _cogc_active flag set by CogC __call__ for reliable detection
(immune to process-global monkey-patching side effects)
- Fix FileInformation.CogC and functions_average to use getattr with
default to prevent AttributeError without CogC loaded
- Extract _add_fundamental_increment helper to deduplicate 5 call sites
- Add defensive assertion after module resolution in lizardcogc.py
- Fix ST comment to document all three excluded END_ keywords
- Remove dead commented-out import in st.py
- Rename test file to testBehavioralRegressions.py (project convention)
- Add regression tests asserting master-baseline token counts for C++,
PLSQL, and Erlang
|
If you have some standard projects you run on, give it a try now. |
This took a lot more than I was expecting. I really like the extension model that Lizard has.
When testing on actual projects, I encountered issues with both Python and PL/SQL, so I am confident we will find other kinks in other languages.
Add Cognitive Complexity (CogC) Metric Support
Overview
This PR implements Cognitive Complexity as a built-in complexity metric alongside Cyclomatic Complexity (CCN) in Lizard. Cognitive Complexity is a metric designed to measure how difficult code is to understand, addressing several limitations of Cyclomatic Complexity by focusing on human intuition about code readability rather than pure mathematical path counting.
Implementation follows the Cognitive Complexity specification v1.2 by SonarSource (April 2017).
What is Cognitive Complexity?
Unlike Cyclomatic Complexity which counts all decision points equally, Cognitive Complexity:
&&or||count as +1, not per operator?.,??) don't add complexityResult: Cognitive Complexity scores better correlate with perceived code difficulty and maintainability.
Implementation Architecture
Extension-Based Design
Cognitive Complexity is implemented as a Lizard extension (
lizard_ext/lizardcogc.py) that:FileInfoBuilderandFunctionInfoto add CogC tracking methodscurrent_nesting_level) for C-like languagescogc_nesting_level) for all languages, especially Python/RubyKey Features Implemented
Four Types of Increments (per specification):
if,for,while,foreach,repeat) - Add +1 + nesting levelelse,elif,elseif) - Add +1 without nesting penalty, but increase nesting for childrengoto, labeled jumps) - Add +1 regardless of nesting#if,#ifdef,#elif) - Treated as structural incrementsSpecial Handling:
cogc_excluded_nestingcounter)ifstatements?.,??)docounted, not trailingwhilecogc_last_operatornormalizationLanguage-Specific Adaptations:
has_top_level_increment)WHENclausespending_structural_nestingflag withadd_bare_nesting()hookLanguage Support
26 languages with Cognitive Complexity support:⚠️ Partial (2/4 parser-limited tests pass)⚠️ Partial (tests skipped)
All Languages however
Erlang
Structured Text
Total:
190 language-specific CogC tests30+ core/spec tests+
= ~220 tests
Files Changed
New Files
lizard_ext/lizardcogc.py- Core CogC extension implementationcognitive_complexity_theory.rst- Comprehensive documentationtest/test_extensions/test_cognitive_complexity.py- Core functionality teststest/test_extensions/test_cognitive_complexity_cli.py- CLI integration testsModified Files
lizard.py- CLI argument for-G/--CogCthreshold (default: 15)lizard_ext/htmloutput.py- Added CogC column to HTML outputREADME.rst- Updated supported languages list, added CogC usage examplesCHANGELOG.md- Documented new featureclike.py,erlang.py,plsql.py,python.py,st.py,typescript.py,zig.pyCLI Usage
Command-Line Options
Output Formats
CogC can appears in all output formats:
<cognitive_complexity>elementcognitive_complexityfieldExample Output
Implementation Details
Token Processing Pipeline
State Variables Tracked
In
FileInfoBuilder:cogc_nesting_level- Control-flow nesting depthcogc_last_operator- Last binary operator (normalized)cogc_nesting_stack- Boolean stack for structural nestingcogc_excluded_nesting- Try block nesting to excludepending_lambda_nesting- Lambda entry flaglambda_depth- Nested lambda trackingpending_structural_nesting- Brace-less language flagIn
FunctionInfo:cognitive_complexity- Running CogC totalinitial_nesting_level- Bracket nesting at function startinitial_cogc_nesting_level- Control-flow nesting at start (preprocessor directives)Nesting Calculation Formula
Documentation
Added Documentation Files
cognitive_complexity_theory.rst- covering:Updated
README.rst:Updated
CHANGELOG.md:Testing Strategy
Test Categories
Core functionality tests (
test_cognitive_complexity.py)CLI integration tests (
test_cognitive_complexity_cli.py)Language-specific tests (26 test files)
Test Coverage Standards
All tests follow strict assertions:
assertEqual(expected, actual.cognitive_complexity)assertGreaterEqual,assertLess, etc.// +1,// +2 (nesting=1)Breaking Changes
None. This is a purely additive feature:
-G/--CogC(doesn't conflict with existing options)Performance Impact
Minimal. The extension:
Future Work
Not implemented (per specification, but low priority):
;in if/case)Specification Attribution
Source Specification
This implementation is based on the Cognitive Complexity specification version 1.2 by G. Ann Campbell, SonarSource SA (April 19, 2017).
Specification Details:
Attribution Locations
Proper attribution to SonarSource and the specification is provided in:
lizard_ext/lizardcogc.py- Core implementation filecognitive_complexity_theory.rst- DocumentationREADME.rst- User-facing documentationTest Examples Inspired by Specification
Test cases inspired by the specification have been transformed to avoid copyright concerns while maintaining specification compliance validation:
Related Issues
Closes #432