Skip to content

Report MA0192 when the compared flag is not a constant - #1315

Merged
meziantou merged 1 commit into
mainfrom
feature/meziantou-analyzer-1313-eb7b25
Aug 20, 2026
Merged

meziantou merged 1 commit into
mainfrom
feature/meziantou-analyzer-1313-eb7b25

Conversation

@meziantou

Copy link
Copy Markdown
Owner

Fixes #1313

What changed

MA0192 (Use HasFlag instead of bitwise checks) only recognized bitwise flag checks whose flag was a constant enum member. (value & comparand) == comparand was not reported when comparand was a parameter, a local variable, or a field.

  • New shared helper src/Meziantou.Analyzer/Rules/UseHasFlagMethodCommon.cs: AreEquivalentOperands structurally compares two operations and accepts only side-effect free references — parameters, locals, non-volatile fields (with an equivalent instance), and this/base. It is picked up by the code fixers project through the existing Rules/*Common.cs glob, so the analyzer and the fixer stay in sync.
  • UseHasFlagMethodAnalyzer.TryGetEnumFlagReference and UseHasFlagMethodFixer.TryGetEnumFlagReference now return IOperation instead of IFieldReferenceOperation and fall back to that equivalence check when the flag is not a constant enum member.
  • Constant-zero operands are excluded from the new path so MA0192 does not overlap with MA0201 (Do not use zero-valued enum flags in flag checks).

The issue repro is now reported and fixed to value.HasFlag(comparand), as are the reversed-operand ((comparand & value) == comparand) and != variants.

Why the restriction to side-effect free references

The code fix replaces two evaluations of the flag expression by a single one. Properties, method calls, and volatile fields may yield a different value on each evaluation, so they are not reported. Tests cover each of those cases.

Note for reviewers

The existing test HasFlagsExtensionZeroFlag_NoDiagnostic contained public static bool HasFlags(this MyEnum value, MyEnum flags) => (value & flags) == flags;, which is exactly the pattern this change is meant to report, so that line now expects MA0192. The subject of that test (no MA0201 on value.HasFlags(MyEnum.None)) is unchanged.

Verification

  • Full test suite on the default Roslyn version: 3736 passed, 0 failed.
  • The 33 UseHasFlagMethodAnalyzerTests pass on roslyn4.8, 4.14, 5.0, 5.6 and 5.9.
  • dotnet run --project src/DocumentationGenerator exits 0 with no further markdown change after updating docs/Rules/MA0192.md.

MA0192 only detected bitwise flag checks whose flag was a constant enum
member, so `(value & comparand) == comparand` was not reported when
`comparand` was a parameter, a local, or a field.

Add a shared `UseHasFlagMethodCommon.AreEquivalentOperands` helper that
structurally compares both operands and only accepts side-effect free
references (parameters, locals, non-volatile fields, `this`/`base`), so
replacing the two evaluations by a single `HasFlag` call cannot change
the behavior. Properties and method calls are not reported.

Constant-zero operands are excluded from the new path so MA0192 does not
overlap with MA0201.

Fixes #1313
@meziantou
meziantou merged commit f127126 into main Aug 20, 2026
12 checks passed
@meziantou
meziantou deleted the feature/meziantou-analyzer-1313-eb7b25 branch August 20, 2026 19:43
This was referenced Aug 20, 2026
This was referenced Sep 21, 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.

MA0192 is not reported when "comparand" is not a constant

1 participant