Parenthesize the expressions produced by the MA0112 and MA0128 fixers - #1385
Merged
Merged
Conversation
Both rules report on the inner invocation, so the fixers replace that node
directly under its parent. The replacements are an '!=' comparison (MA0112)
and an 'is' pattern (MA0128), which bind looser than the invocation they
replace, so splicing them under a unary operator or a member access changed
the meaning of the code and produced CS0023:
if (!list.Any()) { } -> if (!list.Count != 0) { }
if (!str.SequenceEqual("b")) -> if (!str is "b")
list.Any().ToString() -> list.Count != 0.ToString()
Wrap both replacements with Parenthesize(), which annotates the parentheses
with Simplifier.Annotation so the post-processing removes the ones the fixed
document doesn't need. Both fixers also lost the trailing trivia of the
invocation, so carry it over to the new expression.
Note that WithTriviaFrom is not usable here: both fixers reuse the receiver
syntax of the invocation, which already carries the leading trivia, so
copying it again would duplicate it.
This was referenced Sep 6, 2026
Closed
This was referenced Sep 24, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
OptimizeLinqUsageAnalyzer(MA0112) andUseIsPatternInsteadOfSequenceEqualAnalyzer(MA0128) both report on the innerIInvocationOperation, so their fixers get the invocation node andeditor.ReplaceNodesplices the replacement straight under the invocation's parent. The replacements — an!=comparison for MA0112, anispattern for MA0128 — bind looser than the invocation they replace, so under a unary operator or a member access the fix changed the meaning of the code and producedCS0023:!x.Any()is the most common wayAny()is written, so this was the majority case of MA0112's fix rather than an edge case. Both fixers useWellKnownFixAllProviders.BatchFixer, so "Fix all in document" broke every site at once. It went unnoticed because every existing fix test used the bare_ = expr;form, where the replacement happens to need no parentheses.Fix
Wrap both replacements with
Parenthesize()(theMeziantou.Framework.Roslynhelper), which annotates the parentheses withSimplifier.Annotationso the code-fix post-processing removes the ones the final document doesn't need. The existing tests that expect_ = collection.Count != 0;and_ = str is "bar";still pass unchanged.Both fixers also dropped the trailing trivia of the invocation, so it is now carried over to the new expression.
Note for the reviewer
WithTriviaFromis not usable here, even though sibling fixers such asSimplifyNegatedBooleanExpressionFixeruse it. Both of these fixers reuse the receiver syntax of the invocation (invocation.Arguments[0].Syntax/operation.Arguments[0].Value.Syntax), which already carries the leading trivia, so copying it onto the parenthesized node would emit it twice —/* c */(/* c */ x is "bar"). Only the trailing trivia, which sits on the invocation's closing parenthesis, was actually being lost, so only that is copied. TheKeepsTriviatests cover both ends.Tests
Six tests added to the existing test files, covering the negated, member-access and trivia shapes for each rule. Each was run against the unfixed code first and reproduced the exact broken output above.
Any_List_Negated_CodeFix,Any_List_MemberAccess_CodeFix,Any_List_KeepsTrivia_CodeFixReadOnlySpanChar_SequenceEqual_Negated,ReadOnlySpanChar_SequenceEqual_MemberAccess,ReadOnlySpanChar_SequenceEqual_KeepsTriviaVerification
dotnet run --project src/DocumentationGeneratorexits 0 with no markdown changes — this is a behavior-only fix to the code fixers, the rule documentation is unaffected.