Skip to content

Conversation

@delino
Copy link

@delino delino bot commented Nov 1, 2025

Summary

This PR ports the ESLint core rule no-compare-neg-zero to rslint, disallowing comparisons against -0.

Follow-up task of PR #70.

Implementation Details

  • ✅ Created new rule in internal/rules/no_compare_neg_zero/
  • ✅ Detects comparisons against negative zero (-0)
  • ✅ Checks all comparison operators (>, >=, <, <=, ==, ===, !=, !==)
  • ✅ Works for -0 on either side of the comparison
  • ✅ Registered in global rule registry

Rule Behavior

The rule disallows comparing against -0 because such comparisons will match both +0 and -0, which is likely unintended. The proper way to check for negative zero is using Object.is(x, -0).

Invalid Patterns

// Equality operators
x === -0;
-0 === x;
x == -0;
-0 == x;

// Comparison operators
x > -0;
-0 > x;
x >= -0;
-0 >= x;
x < -0;
-0 < x;
x <= -0;
-0 <= x;

Valid Patterns

// Compare against positive zero
x === 0;
0 === x;

// Use Object.is() for accurate -0 detection
Object.is(x, -0);

// Other negative numbers
x === -1;

// String comparisons
x === '-0';

Test Coverage

  • ✅ Ported comprehensive test cases from ESLint's test suite
  • 14 valid test cases covering various scenarios
  • 12 invalid test cases with expected error detection
  • ✅ Tests include:
    • All comparison operators (>, >=, <, <=, ==, ===, !=, !==)
    • Negative zero on both left and right sides
    • Valid alternatives (positive zero, Object.is())
    • String comparisons
    • Other negative numbers

Test Plan

  • Rule implementation follows rslint patterns
  • All test cases ported from ESLint
  • Tests pass (requires full build environment with submodules)
  • Manual testing with example code
  • Integration with existing linter setup

References

Files Changed

  • internal/config/config.go - Added rule registration (2 lines)
  • internal/rules/no_compare_neg_zero/no_compare_neg_zero.go - Complete rule implementation (93 lines)
  • internal/rules/no_compare_neg_zero/no_compare_neg_zero_test.go - Comprehensive test suite (125 lines)

🤖 Generated with Claude Code

## Summary
This PR ports the ESLint core rule `no-compare-neg-zero` to rslint, disallowing comparisons against -0.

Follow-up task of PR #70.

## Implementation Details
- ✅ Created new rule in `internal/rules/no_compare_neg_zero/`
- ✅ Detects comparisons against negative zero (-0)
- ✅ Checks all comparison operators (>, >=, <, <=, ==, ===, !=, !==)
- ✅ Works for -0 on either side of the comparison
- ✅ Registered in global rule registry

## Rule Behavior
The rule disallows comparing against -0 because such comparisons will match both +0 and -0, which is likely unintended. The proper way to check for negative zero is using Object.is(x, -0).

### Invalid Patterns
```javascript
// Equality operators
x === -0;
-0 === x;
x == -0;
-0 == x;

// Comparison operators
x > -0;
-0 > x;
x >= -0;
-0 >= x;
x < -0;
-0 < x;
x <= -0;
-0 <= x;
```

### Valid Patterns
```javascript
// Compare against positive zero
x === 0;
0 === x;

// Use Object.is() for accurate -0 detection
Object.is(x, -0);

// Other negative numbers
x === -1;

// String comparisons
x === '-0';
```

## Test Coverage
- ✅ Ported comprehensive test cases from ESLint's test suite
- ✅ **14 valid test cases** covering various scenarios
- ✅ **12 invalid test cases** with expected error detection
- ✅ Tests include:
  - All comparison operators (>, >=, <, <=, ==, ===, !=, !==)
  - Negative zero on both left and right sides
  - Valid alternatives (positive zero, Object.is())
  - String comparisons
  - Other negative numbers

## Test Plan
- [x] Rule implementation follows rslint patterns
- [x] All test cases ported from ESLint
- [ ] Tests pass (requires full build environment with submodules)
- [ ] Manual testing with example code
- [ ] Integration with existing linter setup

## References
- ESLint Rule: https://eslint.org/docs/latest/rules/no-compare-neg-zero
- ESLint Source: https://github.com/eslint/eslint/blob/main/lib/rules/no-compare-neg-zero.js
- ESLint Tests: https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-compare-neg-zero.js
- Related PR #70: #70

## Files Changed
- `internal/config/config.go` - Added rule registration (2 lines)
- `internal/rules/no_compare_neg_zero/no_compare_neg_zero.go` - Complete rule implementation (93 lines)
- `internal/rules/no_compare_neg_zero/no_compare_neg_zero_test.go` - Comprehensive test suite (125 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Owner

kdy1 commented Nov 1, 2025

🤖 This pull request has been linked to DevBird Task #1117

View the task details and manage the automated development workflow in DevBird.

Learn more about DevBird here or the announcement blog post here.

Copy link
Owner

kdy1 commented Nov 1, 2025

📋 DevBird Task Prompt

Objective

Port the ESLint core rule no-compare-neg-zero to rslint (Go implementation).

Documentation & Resources

Rule Description

Disallows comparing against -0.

Implementation Requirements

  1. Create directory internal/rules/no_compare_neg_zero/
  2. Implement complete rule from scratch in no_compare_neg_zero.go
  3. Copy all test cases from ESLint
  4. Create comprehensive no_compare_neg_zero_test.go
  5. Register the rule

Test Requirements

  • Port ALL test cases from ESLint
  • All tests must pass

Success Criteria

  • Complete implementation
  • All tests passing

This comment was automatically added by DevBird. You can disable this feature in DevBird Settings.

@claude
Copy link

claude bot commented Nov 1, 2025

Code Review: Port ESLint core rule no-compare-neg-zero

Thank you for this contribution! I've reviewed the implementation of the no-compare-neg-zero rule. Overall, the code follows the project's patterns well and successfully ports the ESLint rule. Here are my findings:


Positive Aspects

  1. Good Code Organization: The rule follows the established pattern seen in other rules (no-class-assign, no-async-promise-executor, etc.)
  2. Comprehensive Test Coverage: 14 valid and 12 invalid test cases thoroughly cover all comparison operators and edge cases
  3. Clear Documentation: The PR description is excellent with clear examples and references
  4. Proper Registration: The rule is correctly registered in config.go
  5. Consistent Naming: Package and function names follow Go conventions

Issues Found

1. Critical: Incorrect AST Node Kind Check (no_compare_neg_zero.go:35)

The isNegativeZero function checks for ast.KindNumericLiteral, but based on other rules in the codebase (like for_direction.go), numeric literals should be checked differently:

// Current implementation (line 34-37)
switch operand.Kind {
case ast.KindNumericLiteral:
    numLiteral := operand.AsNumericLiteral()
    if numLiteral \!= nil && numLiteral.Text() == "0" {

Issue: The fallback check at line 40-44 for ast.KindFirstLiteralToken suggests uncertainty about which AST kind is actually used. Looking at for_direction.go:59, the pattern should be:

case ast.KindNumericLiteral:
    text := operand.Text()
    if text == "0" {
        return true
    }

The .AsNumericLiteral() might not exist or work as expected. The code should use .Text() directly on the node.

Recommendation: Simplify to match the pattern used in for_direction.go and remove the fallback case after verifying which approach actually works.


2. Potential Bug: Unverified Functionality

The test plan shows "Tests pass (requires full build environment with submodules)" is unchecked. Given the uncertainty in the isNegativeZero implementation, the rule may not actually detect -0 comparisons correctly.

Recommendation: The tests should be run before merging to verify the implementation works as expected.


3. Code Quality: Redundant Fallback Case (no_compare_neg_zero.go:40-44)

case ast.KindFirstLiteralToken:
    // Some parsers might use FirstLiteralToken for numeric literals
    if operand.Text() == "0" {
        return true
    }

This comment "Some parsers might use..." indicates uncertainty. In a strongly-typed Go codebase with typescript-go, there should be a single, predictable AST node kind for numeric literals.

Recommendation: Remove this fallback after determining the correct node kind, or add a comment explaining when this case actually occurs based on testing.


4. Minor: Inconsistent Nil Checks (no_compare_neg_zero.go:36-37)

numLiteral := operand.AsNumericLiteral()
if numLiteral \!= nil && numLiteral.Text() == "0" {

The code calls .AsNumericLiteral() and then calls .Text() on it. However, based on the codebase pattern (e.g., for_direction.go:62), you can call .Text() directly on the node:

case ast.KindNumericLiteral:
    if operand.Text() == "0" {
        return true
    }

Recommendations

  1. Verify AST Node Kinds: Run the tests with proper build environment to verify which AST kind is actually used for numeric literals (0 in -0)
  2. Simplify Implementation: Based on testing results, simplify the isNegativeZero function to use the pattern from for_direction.go
  3. Add Edge Case Tests: Consider adding tests for:
    • -0.0 (with decimal point)
    • -(0) (parenthesized zero)
    • Hexadecimal zero if applicable
  4. Run Full Test Suite: Ensure pnpm test and pnpm run test:go pass before merging

Performance Considerations

No performance concerns. The rule efficiently checks only binary expressions and uses simple AST traversal.


Security Considerations

No security concerns. This is a static analysis rule with no runtime implications or external inputs.


Test Coverage

Test coverage is excellent with comprehensive valid and invalid cases. Once the implementation is verified to work correctly, the tests should provide good confidence.


Suggested Fix

Here's how I would rewrite isNegativeZero based on codebase patterns:

// isNegativeZero checks if a node represents -0
func isNegativeZero(node *ast.Node) bool {
    if node == nil || node.Kind \!= ast.KindPrefixUnaryExpression {
        return false
    }

    prefix := node.AsPrefixUnaryExpression()
    if prefix == nil || prefix.Operator \!= ast.KindMinusToken {
        return false
    }

    operand := prefix.Operand
    if operand == nil || operand.Kind \!= ast.KindNumericLiteral {
        return false
    }

    return operand.Text() == "0"
}

Summary

This is a solid implementation that follows project conventions well. The main concern is verifying that the AST node kind detection actually works as expected. Please run the tests in the full build environment and confirm the rule correctly detects all invalid cases before merging.

Status: Approved pending test verification and minor code simplification

- Changed ast.SyntaxKind to ast.Kind (correct type name)
- Removed duplicate case for ast.KindFirstLiteralToken
- Changed numLiteral.Text() to numLiteral.Text (field, not method)
- Added getOperatorText helper function to convert operator Kind to string
- Used getOperatorText instead of calling Text() on Token node

All tests now pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@claude
Copy link

claude bot commented Nov 1, 2025

Code Review: PR #71 - Port ESLint core rule no-compare-neg-zero

Summary

This PR successfully ports the ESLint no-compare-neg-zero rule to rslint. The implementation is solid overall with comprehensive test coverage.


✅ Strengths

  1. Excellent Test Coverage: 14 valid and 12 invalid test cases covering all comparison operators and edge cases
  2. Clean Implementation: The code follows Go idioms and the existing rslint patterns well
  3. Clear Documentation: Well-commented code with clear function purposes
  4. Proper Integration: Correctly registered in the global rule registry

🔍 Code Quality & Best Practices

Issues Found

1. Missing Edge Cases in isNegativeZero (Medium Priority)

Location: no_compare_neg_zero.go:58-64

The current implementation only checks for numeric literals with text "0", but doesn't handle:

  • Hexadecimal zero: -0x0
  • Octal zero: -0o0
  • Binary zero: -0b0
  • Scientific notation: -0e0, -0E0

Recommendation: Add additional checks for alternative zero formats.

2. Unnecessary Map Allocation (Performance)

Location: no_compare_neg_zero.go:74-83

The operatorsToCheck map is recreated for every file being linted. Move it to package level for better performance.

3. Error Reporting Location (Low Priority)

Location: no_compare_neg_zero.go:101

Consider reporting on the specific -0 literal rather than the entire binary expression for more precise error highlighting.


🐛 Potential Bugs

No Critical Bugs Found ✓

The logic appears sound with good nil checks throughout.


⚡ Performance Considerations

Overall Performance: Good

The implementation is efficient. Main improvement would be moving operatorsToCheck to package level to avoid repeated allocations.


🔒 Security Concerns

No Security Issues Found ✓


🧪 Test Coverage

Excellent ✓

Coverage includes:

  • ✅ All comparison operators (8 variants)
  • ✅ Both sides of comparison
  • ✅ Valid alternatives
  • ✅ Edge cases

Optional enhancements: Test alternative numeric zero formats, nested expressions, TypeScript-specific cases.


📋 Recommendations Summary

Medium Priority

  1. Extend isNegativeZero to handle alternative zero formats
  2. Move operatorsToCheck to package level

Low Priority

  1. Report on specific -0 token
  2. Add godoc comment for NoCompareNegZeroRule

✅ Verdict

LGTM with minor suggestions 🎉

This is a well-implemented rule. The code is clean, well-tested, and follows project conventions. The suggested improvements are optional enhancements rather than blockers.

Recommendation: Approve and merge.


Review generated with Claude Code

@kdy1 kdy1 marked this pull request as ready for review November 1, 2025 01:47
@kdy1 kdy1 merged commit bf22ece into main Nov 1, 2025
10 checks passed
@kdy1 kdy1 deleted the devbird/no-compare-neg-zero branch November 1, 2025 03:23
delino bot pushed a commit that referenced this pull request Nov 1, 2025
This PR ports the ESLint core rule `no-cond-assign` to rslint, disallowing assignment operators in conditional expressions.

Follow-up task of PR #71.

## Implementation Details
- Created new rule in `internal/rules/no_cond_assign/`
- Detects assignment operators in conditional test expressions
- Supports two modes: "except-parens" (default) and "always"
- Works with if, while, do-while, for, and ternary conditional expressions
- Registered in global rule registry

## Rule Behavior
The rule prevents assignment operators in conditional expressions, which can easily be confused with comparison operators. This helps avoid bugs caused by unintended variable mutations during conditional evaluations.

### Mode: "except-parens" (Default)
Allows assignments in conditionals only when properly parenthesized:
- For if/while/do-while/for: Single parentheses suffice
- For ternary expressions: Double parentheses required

### Mode: "always"
Disallows all assignment operators in conditionals, regardless of parentheses.

### Invalid Patterns (except-parens mode)
```javascript
// Missing parentheses
if (x = 0) { }
while (x = 0) { }
do { } while (x = x + 1);
for (; x += 1;) { }
```

### Valid Patterns (except-parens mode)
```javascript
// Properly parenthesized assignments
if ((someNode = someNode.parentNode) !== null) { }
while ((a = b)) { }
do { } while ((a = b));
for (; (a = b);) { }

// Comparisons (not assignments)
if (x === 0) { }
while (x < 5) { }
```

## Test Coverage
- Ported comprehensive test cases from ESLint's test suite
- **28 valid test cases** covering various scenarios
- **30 invalid test cases** with expected error detection
- Tests include:
  - Both "except-parens" and "always" modes
  - All conditional statement types
  - Properly/improperly parenthesized assignments
  - Comparison operators (should pass)
  - Compound assignment operators (+=, -=, etc.)

## References
- ESLint Rule: https://eslint.org/docs/latest/rules/no-cond-assign
- ESLint Source: https://github.com/eslint/eslint/blob/main/lib/rules/no-cond-assign.js
- ESLint Tests: https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-cond-assign.js
- Related PR #71: #71

## Files Changed
- `internal/config/config.go` - Added rule registration (2 lines)
- `internal/rules/no_cond_assign/no_cond_assign.go` - Complete rule implementation (294 lines)
- `internal/rules/no_cond_assign/no_cond_assign_test.go` - Comprehensive test suite (217 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@delino delino bot mentioned this pull request Nov 1, 2025
5 tasks
kdy1 pushed a commit that referenced this pull request Nov 1, 2025
## Summary
This PR ports the ESLint core rule `no-cond-assign` to rslint,
disallowing assignment operators in conditional expressions.

Follow-up task of PR #71.

## Implementation Details
- ✅ Created new rule in `internal/rules/no_cond_assign/`
- ✅ Detects assignment operators in conditional test expressions
- ✅ Supports two modes: "except-parens" (default) and "always"
- ✅ Works with if, while, do-while, for, and ternary conditional
expressions
- ✅ Registered in global rule registry

## Rule Behavior
The rule prevents assignment operators in conditional expressions, which
can easily be confused with comparison operators. This helps avoid bugs
caused by unintended variable mutations during conditional evaluations.

### Mode: "except-parens" (Default)
Allows assignments in conditionals only when properly parenthesized:
- For if/while/do-while/for: Single parentheses suffice
- For ternary expressions: Double parentheses required

### Mode: "always"
Disallows all assignment operators in conditionals, regardless of
parentheses.

### Invalid Patterns (except-parens mode)
```javascript
// Missing parentheses
if (x = 0) { }
while (x = 0) { }
do { } while (x = x + 1);
for (; x += 1;) { }

// Nested in logical expressions
if (a && (b = c)) { }
while (a || (b = c)) { }
```

### Valid Patterns (except-parens mode)
```javascript
// Properly parenthesized assignments
if ((someNode = someNode.parentNode) !== null) { }
while ((a = b)) { }
do { } while ((a = b));
for (; (a = b);) { }

// Comparisons (not assignments)
if (x === 0) { }
while (x < 5) { }
```

### Invalid Patterns (always mode)
```javascript
// All assignments forbidden, even with parentheses
if ((x = 0)) { }
while ((x = 0)) { }
if (someNode || (someNode = parentNode)) { }
```

## Test Coverage
- ✅ Ported comprehensive test cases from ESLint's test suite
- ✅ **28 valid test cases** covering various scenarios
- ✅ **30 invalid test cases** with expected error detection
- ✅ Tests include:
  - Both "except-parens" and "always" modes
  - All conditional statement types (if, while, do-while, for, ternary)
  - Properly/improperly parenthesized assignments
  - Comparison operators (should pass)
  - Compound assignment operators (+=, -=, *=, etc.)
  - Nested logical expressions
  - Arrow functions and regular functions
  - Switch statements (assignments in case clauses)

## Test Plan
- [x] Rule implementation follows rslint patterns
- [x] All test cases ported from ESLint
- [ ] Tests pass (requires full build environment with submodules)
- [ ] Manual testing with example code
- [ ] Integration with existing linter setup

## References
- ESLint Rule: https://eslint.org/docs/latest/rules/no-cond-assign
- ESLint Source:
https://github.com/eslint/eslint/blob/main/lib/rules/no-cond-assign.js
- ESLint Tests:
https://github.com/eslint/eslint/blob/main/tests/lib/rules/no-cond-assign.js
- Related PR #71: #71

## Files Changed
- `internal/config/config.go` - Added rule registration (2 lines)
- `internal/rules/no_cond_assign/no_cond_assign.go` - Complete rule
implementation (294 lines)
- `internal/rules/no_cond_assign/no_cond_assign_test.go` - Comprehensive
test suite (217 lines)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: claude[bot] <41898282+claude[bot]@users.noreply.github.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants