-
-
Notifications
You must be signed in to change notification settings - Fork 110
fix: resolve multiple migration code fixer bugs #4557
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
This commit addresses several issues in the NUnit and xUnit migration code fixers: - Fix using directive removal in namespace blocks (#4486, #4482) Extended RemoveFrameworkUsings() to also remove using directives from NamespaceDeclarationSyntax and FileScopedNamespaceDeclarationSyntax - Fix preprocessor directive corruption (#4481, #4487, #4488, #4492) Updated CleanupClassMemberLeadingTrivia() to preserve #region, #if, #endif, #pragma, and other preprocessor directives. Only removes excessive consecutive blank lines, not all newlines. - Fix Platform attribute conversion (#4489) Removed conversion that generated non-existent [ExcludeOn] attribute. Platform attribute is now removed during migration. - Fix FixtureLifeCycle attribute handling (#4490) Added FixtureLifeCycle to recognized and removable attributes. TUnit uses instance-per-test by default. - Fix Assert.Throws tracking (#4483) Updated ConvertThrows/ConvertThrowsAsync to return original expression instead of null, ensuring assertions are tracked in conversion plan. - Fix async return type wrapping (#4491) Added WrapReturnTypeInTask support for non-void, non-Task return types. Methods returning object/int now correctly become Task<object>/Task<int>. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SummaryFixes multiple migration code fixer bugs related to trivia handling, attribute conversion, assertion tracking, and async return type wrapping. Critical IssuesNone found ✅ Suggestions1. Potential infinite loop in CleanupClassMemberLeadingTrivia (Low Risk)In Consider this edge case: class Test {
#region Test
[Test]
void Method() { }
}After the first pass, if a single newline remains after the #region, the method won't detect it as "excessive" (needs >1 consecutive), but Recommendation: Add a unit test with preprocessor directives to verify the cleanup loop terminates and preserves formatting correctly. 2. AnalyzeReturnTypeForAsync string comparison robustnessIn Recommendation: Consider using semantic model or more robust pattern matching if this becomes an issue in testing. The current approach should work for most cases since 3. Assert.Throws tracking expression consistencyIn This is fine for tracking purposes, but be aware the tracked text may differ slightly from the original source. Previous Review StatusUnable to fetch previous comments (API permissions), but this appears to be a new PR addressing multiple issues (#4486, #4482, #4481, #4487, #4488, #4492, #4489, #4490, #4483, #4491). Verdict✅ APPROVE - No critical issues TUnit Rules Checklist:
|
TUnit does have ExcludeOnAttribute, so the Platform attribute conversion should be kept. Only removed Platform from NUnitRemovableAttributeNames. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
SummaryFixes multiple migration code fixer bugs: using directive removal in namespaces, preprocessor directive preservation, FixtureLifeCycle attribute handling, Assert.Throws tracking, and async return type wrapping. Critical IssuesNone found ✅ Suggestions1. Preprocessor directive trivia logic complexityIn BaseMigrationCodeFixProvider.cs:401-458, the new trivia handling logic is more sophisticated but also more complex. The loop that builds triviaToKeep tracks state with consecutiveNewlines and lastWasPreprocessorOrComment. Edge case concern: After a preprocessor directive like #region, the code allows one newline (consecutiveNewlines == 1), but then resets lastWasPreprocessorOrComment = false on the newline. This means a second newline would be considered excessive and removed. However, the detection logic in FindClassWithExcessiveLeadingTrivia requires consecutiveNewlines > 1 to detect excessive trivia. This creates a subtle interaction where first pass removes excessive newlines and after first pass may leave preprocessor + 1 newline which won't trigger another pass. This appears intentional and correct, but worth verifying with tests that have multiple preprocessor directives. 2. String-based return type analysisIn MigrationAnalyzer.cs:567-592, the AnalyzeReturnTypeForAsync method uses string comparisons like returnTypeText.StartsWith(Task<). While this works for typical cases, it may not handle fully qualified types or type aliases. The current approach should work for 99% of cases since ReturnType.ToString() typically gives the short form. 3. Assert.Throws expression reconstructionIn XUnitTwoPhaseAnalyzer.cs:332-369, the code now reconstructs expressions for tracking purposes using .ToString() on arguments. Lambda expressions with line breaks will be flattened and formatting may differ slightly from original. This is acceptable since it's only for tracking purposes. Verdict✅ APPROVE - No critical issues TUnit Rules Applied: No core engine changes, no snapshot test changes needed, no VSTest usage, no performance concerns, no reflection requiring AOT annotations, no security vulnerabilities detected. |
Summary
Fix using directive removal in namespace blocks: Extended
RemoveFrameworkUsings()to also remove using directives from inside namespace blocks (both classic and file-scoped namespaces)Fix preprocessor directive corruption: Updated
CleanupClassMemberLeadingTrivia()to preserve#region,#if,#endif,#pragma, and other preprocessor directives. Only removes excessive consecutive blank lines, not all newlines.Fix FixtureLifeCycle attribute handling: Added
FixtureLifeCycleto recognized and removable attributes. TUnit uses instance-per-test by default, matching NUnit'sInstancePerTestCase.Fix Assert.Throws tracking: Updated
ConvertThrows/ConvertThrowsAsyncto return the original expression instead of null, ensuring assertions are properly tracked in the conversion plan.Fix async return type wrapping: Added
WrapReturnTypeInTasksupport for non-void, non-Task return types. Methods returningobject,int, etc. now correctly becomeasync Task<object>,async Task<int>, etc.Closes
Fixes #4481
Fixes #4482
Fixes #4483
Fixes #4486
Fixes #4487
Fixes #4488
Fixes #4490
Fixes #4491
Fixes #4492
Note
Issue #4489 (Platform attribute conversion) was found to be invalid - TUnit does have
ExcludeOnAttribute, so the existing conversion is correct.Test plan
🤖 Generated with Claude Code