Skip to content

Add new early return sniff when function is just a return var declara…#2851

Merged
Crabcyborg merged 3 commits into
masterfrom
add_new_early_return_sniff_when_function_is_var_declarion_if_and_return
Jan 21, 2026
Merged

Add new early return sniff when function is just a return var declara…#2851
Crabcyborg merged 3 commits into
masterfrom
add_new_early_return_sniff_when_function_is_var_declarion_if_and_return

Conversation

@Crabcyborg
Copy link
Copy Markdown
Contributor

@Crabcyborg Crabcyborg commented Jan 21, 2026

…tion and if

This fixes issues in Pro, but it doesn't look to be an issue in Lite.

Summary by CodeRabbit

  • New Features

    • Added a new code analysis rule to detect and suggest refactoring for specific conditional code patterns.
  • Chores

    • Updated analysis ruleset configuration to include enhanced code quality validation.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jan 21, 2026

📝 Walkthrough

Walkthrough

Introduces a new PHP_CodeSniffer sniff that detects patterns where a variable is initialized, followed by an if block, and the function returns that variable. The sniff identifies such patterns and provides an automatic fixer to transform them into early-return structures when the if body contains a minimum number of statements (default: 5). The ruleset.xml is reorganized with updated sniff registrations.

Changes

Cohort / File(s) Summary
New Sniff Implementation
phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php
Introduces 677 lines implementing pattern detection and transformation logic: identifies variable initialization → if block → return patterns; extracts and validates initial values; counts statements in if blocks; negates conditions using operator flipping; applies multi-step token-level transformations to convert to early-return form with automatic fixer. Includes helpers for condition negation, comparison operator manipulation, code dedentation, and indentation inference.
Ruleset Configuration
phpcs-sniffs/Formidable/ruleset.xml
Reorganizes CodeAnalysis sniff registrations: reorders rules including FlipIfToEarlyReturnInitialValue, RedundantIsArrayBeforeEmptyArrayCheck, PreferNegationOverEmptyForFunctionCall, PreferEmptyArrayComparison, and Move-related sniffs. Changes are net-neutral (additions and removals of same rules with positional adjustments).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A sniff that's quite clever and neat,
Turns long ifs into early retreat!
With conditions flipped clean and returns early seen,
The code becomes tighter—a victory sweet! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: introducing a new early-return sniff for functions with variable declaration, if statement, and return pattern.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Crabcyborg Crabcyborg merged commit 727c548 into master Jan 21, 2026
14 of 15 checks passed
@Crabcyborg Crabcyborg deleted the add_new_early_return_sniff_when_function_is_var_declarion_if_and_return branch January 21, 2026 01:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php`:
- Around line 519-549: negation for simple non-flippable comparisons in
negateCondition produces '! $x > 0' which breaks precedence, and
flipComparisonOperator is missing mappings for '<' and '>' so they never get
flipped; update negateCondition to wrap the fallback negation in parentheses
(return '! ( ' . $condition . ' )' instead of '! ' . $condition) and extend
flipComparisonOperator to include mappings for '>' => '<=', '>=' => '<', '<' =>
'>=', and '<=' => '>' (and their spaced variants if the method normalizes
spacing) so comparisons like $x > 0 and $x < 0 are correctly flipped rather than
left to the fallback.
🧹 Nitpick comments (1)
phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php (1)

319-336: Drop unused $phpcsFile parameters in helpers.

countStatementsInScope() and getConditionString() don’t use $phpcsFile, which triggers PHPMD. Remove the parameter and update call sites.

♻️ Proposed refactor
-        $statementCount = $this->countStatementsInScope( $phpcsFile, $tokens, $ifToken );
+        $statementCount = $this->countStatementsInScope( $tokens, $ifToken );

-        $condition = $this->getConditionString( $phpcsFile, $tokens, $ifToken );
+        $condition = $this->getConditionString( $tokens, $ifToken );
-    private function countStatementsInScope( File $phpcsFile, array $tokens, $scopeToken ) {
+    private function countStatementsInScope( array $tokens, $scopeToken ) {
-    private function getConditionString( File $phpcsFile, array $tokens, $ifToken ) {
+    private function getConditionString( array $tokens, $ifToken ) {

Also applies to: 347-368

Comment on lines +519 to +549
private function negateCondition( $condition ) {
$condition = trim( $condition );

// Check if this is a compound condition (has && or || at top level).
$isCompound = $this->hasTopLevelOperator( $condition );

// Only remove leading ! if it's a simple condition (not compound).
if ( ! $isCompound ) {
// If condition starts with !, remove it.
if ( strpos( $condition, '! ' ) === 0 ) {
return substr( $condition, 2 );
}

if ( strpos( $condition, '!' ) === 0 && strpos( $condition, '!=' ) !== 0 ) {
return substr( $condition, 1 );
}

// Try to flip comparison operators.
$flipped = $this->flipComparisonOperator( $condition );

if ( $flipped !== false ) {
return $flipped;
}

// Simple condition, just add !
return '! ' . $condition;
}

// For compound conditions, wrap in parentheses and negate.
return '! ( ' . $condition . ' )';
}
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

PHP operator precedence ! vs comparison operators

💡 Result:

The unary NOT operator (!) has higher precedence than PHP's comparison operators (==, ===, <, >, <=, >=). So ! binds first:

  • !$a == $b is parsed as (! $a) == $b.
  • To negate a comparison you must parenthesize: !($a == $b).

Source: PHP operator precedence table. [1]

References
[1] PHP Manual — Operator Precedence.


🏁 Script executed:

# Find and examine the PHP file to verify the claims
fd "FlipIfToEarlyReturnInitialValueSniff.php" --type f

Repository: Strategy11/formidable-forms

Length of output: 155


🏁 Script executed:

# Read the PHP file to verify the code and operator map
wc -l phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php

Repository: Strategy11/formidable-forms

Length of output: 159


🏁 Script executed:

# Get the negateCondition function and surrounding code (lines 519-549)
sed -n '510,560p' phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php

Repository: Strategy11/formidable-forms

Length of output: 1394


🏁 Script executed:

# Check lines 600-623 for related code
sed -n '595,630p' phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php

Repository: Strategy11/formidable-forms

Length of output: 1006


🏁 Script executed:

# Find the operatorMap definition
rg -n "operatorMap" phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php -A 10

Repository: Strategy11/formidable-forms

Length of output: 688


Fix negation for </> to avoid operator precedence bugs in the fixer.

For conditions like $x > 0, the fallback at line 544 emits ! $x > 0, which PHP parses as (! $x) > 0 due to ! having higher precedence than comparison operators. This changes semantics. Additionally, the flipComparisonOperator() method (lines 600–623) lacks entries for < and > operators, preventing them from being flipped. Wrap the fallback negation in parentheses and add both operators to the map.

Proposed fix
-            // Simple condition, just add !
-            return '! ' . $condition;
+            // Simple condition: wrap to avoid precedence surprises.
+            return '! ( ' . $condition . ' )';
         $operatorMap = array(
             '!==' => '===',
             '===' => '!==',
+            '>'   => '<=',
+            '<'   => '>=',
             '!='  => '==',
             '=='  => '!=',
             '>='  => '<',
             '<='  => '>',
         );
🤖 Prompt for AI Agents
In
`@phpcs-sniffs/Formidable/Sniffs/CodeAnalysis/FlipIfToEarlyReturnInitialValueSniff.php`
around lines 519 - 549, negation for simple non-flippable comparisons in
negateCondition produces '! $x > 0' which breaks precedence, and
flipComparisonOperator is missing mappings for '<' and '>' so they never get
flipped; update negateCondition to wrap the fallback negation in parentheses
(return '! ( ' . $condition . ' )' instead of '! ' . $condition) and extend
flipComparisonOperator to include mappings for '>' => '<=', '>=' => '<', '<' =>
'>=', and '<=' => '>' (and their spaced variants if the method normalizes
spacing) so comparisons like $x > 0 and $x < 0 are correctly flipped rather than
left to the fallback.

stephywells pushed a commit that referenced this pull request Apr 4, 2026
…hen_function_is_var_declarion_if_and_return

Add new early return sniff when function is just a return var declara…
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant