Skip to content

Do not report MA0148/MA0149 when the constant does not convert to the operand type - #1470

Merged
meziantou merged 2 commits into
mainfrom
feature/ma0148-numeric-promotion-pattern-209ec7
Sep 12, 2026
Merged

meziantou merged 2 commits into
mainfrom
feature/ma0148-numeric-promotion-pattern-209ec7

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Problem

MA0148/MA0149 rewrite value == 1L (with int value) into value is 1L, which fails with CS0266. The binary equality operator promotes the int operand to long, but the fixer copies the operand syntax into an is expression, dropping the implicit conversion. Constant patterns are matched against the operand's own type, so the long constant is not valid there.

The existing guard only excluded implicit user-defined conversions, not built-in numeric promotion.

Fix

When the comparison implicitly converts the operand, the analyzer and the fixer now check that the constant is implicitly convertible (without user-defined conversion) to the type of the operand as written in source (or its underlying type when nullable). If not, the comparison is not reported nor rewritten. This check replaces HasImplicitUserDefinedConversion, which it covers.

  • Still reported/fixed: byteValue == 1byteValue is 1 (same for short, ushort, byte?)
  • No longer reported: int vs 1L / 1.0 / 1m, int? vs 1L, float vs 0.1, byte vs 300, char vs 65
  • Merged chains keep the valid terms only: value == 1 || value == 2Lvalue is 1 || value == 2L

The fixer shares the check for both single comparisons and merged ||/&& candidates, so a fix triggered on a valid term can no longer merge an invalid one.

Note for reviewers

The operand type is taken from SemanticModel.GetTypeInfo(operandSyntax).Type rather than by unwrapping the conversion operations: Roslyn 4.8 represents (DayOfWeek?)1 as a chain of implicit conversions down to the int literal, so unwrapping overshoots and broke the existing EqualityComparison_NullableEnum test on that version.

Tests

  • New tests in UsePatternMatchingForEqualityComparisonsAnalyzerTests for non-convertible constants, convertible promoted operands, and mixed/merged comparisons (verified that 9 of the new cases fail without the fix).
  • MA0148/MA0149 tests pass on Roslyn 4.8, 4.14, 5.0, 5.6 and 5.9 (46/46 each).
  • Full test suite passes on Roslyn 5.9 (4394/4394).
  • docs/Rules/MA0148.md and docs/Rules/MA0149.md document the new exclusion; DocumentationGenerator reports no further changes.

… operand type

The equality operator may implicitly convert the operand (numeric promotion,
nullable lifting, user-defined conversions), while the pattern is matched
against the type of the operand itself. For instance, 'intValue == 1L' is
valid but 'intValue is 1L' fails with CS0266.

The analyzer and the code fixer (single and merged comparisons) now only
rewrite the comparison when the constant is implicitly convertible to the
type of the operand, without user-defined conversion. This replaces the
previous check that only excluded implicit user-defined conversions.
Comment thread src/Meziantou.Analyzer/Rules/UsePatternMatchingForEqualityComparisonsCommon.cs Outdated
… model

The type of the operand can be read from the operation tree. The semantic
model is still needed to classify the conversion of the constant, as it
depends on its value ('byteValue == 1' is valid, 'byteValue == 300' is not).
@meziantou
meziantou merged commit c223e4f into main Sep 12, 2026
13 checks passed
@meziantou
meziantou deleted the feature/ma0148-numeric-promotion-pattern-209ec7 branch September 12, 2026 03:35
This was referenced Sep 12, 2026
This was referenced Sep 17, 2026
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.

1 participant