10
10
using System . ComponentModel . Composition ;
11
11
using Microsoft . CodeAnalysis . CSharp ;
12
12
using Microsoft . CodeAnalysis . CodeGeneration ;
13
+ using System . Runtime . Serialization ;
13
14
14
15
namespace Microsoft . DotNet . CodeFormatting . Rules
15
16
{
@@ -70,6 +71,7 @@ public async Task<Document> ProcessAsync(Document document, CancellationToken ca
70
71
TransformationTracker transformationTracker = new TransformationTracker ( ) ;
71
72
RemoveTestClassAttributes ( root , semanticModel , transformationTracker ) ;
72
73
ChangeTestMethodAttributesToFact ( root , semanticModel , transformationTracker ) ;
74
+ ChangeAssertCalls ( root , semanticModel , transformationTracker ) ;
73
75
root = transformationTracker . TransformRoot ( root ) ;
74
76
75
77
@@ -121,7 +123,7 @@ private void RemoveTestClassAttributes(CompilationUnitSyntax root, SemanticModel
121
123
nodesToRemove . AddRange ( attributesToRemove ) ;
122
124
}
123
125
124
- transformationTracker . AddTransformation ( nodesToRemove , ( transformationRoot , rewrittenNodes ) =>
126
+ transformationTracker . AddTransformation ( nodesToRemove , ( transformationRoot , rewrittenNodes , originalNodeMap ) =>
125
127
{
126
128
foreach ( AttributeSyntax rewrittenNode in rewrittenNodes )
127
129
{
@@ -157,14 +159,82 @@ private void ChangeTestMethodAttributesToFact(CompilationUnitSyntax root, Semant
157
159
}
158
160
}
159
161
160
- transformationTracker . AddTransformation ( nodesToReplace , ( transformationRoot , rewrittenNodes ) =>
162
+ transformationTracker . AddTransformation ( nodesToReplace , ( transformationRoot , rewrittenNodes , originalNodeMap ) =>
161
163
{
162
164
return transformationRoot . ReplaceNodes ( rewrittenNodes , ( originalNode , rewrittenNode ) =>
163
165
{
164
166
return ( ( AttributeSyntax ) rewrittenNode ) . WithName ( SyntaxFactory . ParseName ( "Fact" ) ) . NormalizeWhitespace ( ) ;
165
167
} ) ;
166
168
} ) ;
167
-
169
+ }
170
+
171
+ private void ChangeAssertCalls ( CompilationUnitSyntax root , SemanticModel semanticModel , TransformationTracker transformationTracker )
172
+ {
173
+ Dictionary < string , string > assertMethodsToRename = new Dictionary < string , string > ( )
174
+ {
175
+ { "AreEqual" , "Equal" } ,
176
+ { "AreNotEqual" , "NotEqual" } ,
177
+ { "IsNull" , "Null" } ,
178
+ { "IsNotNull" , "NotNull" } ,
179
+ { "AreSame" , "Same" } ,
180
+ { "AreNotSame" , "NotSame" } ,
181
+ { "IsTrue" , "True" } ,
182
+ { "IsFalse" , "False" } ,
183
+ { "IsInstanceOfType" , "IsAssignableFrom" } ,
184
+
185
+ } ;
186
+
187
+ Dictionary < SimpleNameSyntax , string > nameReplacementsForNodes = new Dictionary < SimpleNameSyntax , string > ( ) ;
188
+ List < InvocationExpressionSyntax > methodCallsToReverseArguments = new List < InvocationExpressionSyntax > ( ) ;
189
+
190
+ foreach ( var methodCallSyntax in root . DescendantNodes ( ) . OfType < MemberAccessExpressionSyntax > ( ) )
191
+ {
192
+ var expressionSyntax = methodCallSyntax . Expression ;
193
+ var expressionTypeInfo = semanticModel . GetTypeInfo ( expressionSyntax ) ;
194
+ if ( expressionTypeInfo . Type != null )
195
+ {
196
+ string expressionDocID = expressionTypeInfo . Type . GetDocumentationCommentId ( ) ;
197
+ if ( expressionDocID == "T:Microsoft.VisualStudio.TestTools.UnitTesting.Assert" )
198
+ {
199
+ string newMethodName ;
200
+ if ( assertMethodsToRename . TryGetValue ( methodCallSyntax . Name . Identifier . Text , out newMethodName ) )
201
+ {
202
+ nameReplacementsForNodes . Add ( methodCallSyntax . Name , newMethodName ) ;
203
+
204
+ if ( newMethodName == "IsAssignableFrom" && methodCallSyntax . Parent is InvocationExpressionSyntax )
205
+ {
206
+ // Parameter order is reversed between MSTest Assert.IsInstanceOfType and xUnit Assert.IsAssignableFrom
207
+ methodCallsToReverseArguments . Add ( ( InvocationExpressionSyntax ) methodCallSyntax . Parent ) ;
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+
214
+ if ( nameReplacementsForNodes . Any ( ) )
215
+ {
216
+ transformationTracker . AddTransformation ( nameReplacementsForNodes . Keys , ( transformationRoot , rewrittenNodes , originalNodeMap ) =>
217
+ {
218
+ return transformationRoot . ReplaceNodes ( rewrittenNodes , ( originalNode , rewrittenNode ) =>
219
+ {
220
+ var realOriginalNode = ( SimpleNameSyntax ) originalNodeMap [ originalNode ] ;
221
+ string newName = nameReplacementsForNodes [ realOriginalNode ] ;
222
+ return SyntaxFactory . ParseName ( newName ) ;
223
+ } ) ;
224
+ } ) ;
225
+
226
+ transformationTracker . AddTransformation ( methodCallsToReverseArguments , ( transformationRoot , rewrittenNodes , originalNodeMap ) =>
227
+ {
228
+ return transformationRoot . ReplaceNodes ( rewrittenNodes , ( originalNode , rewrittenNode ) =>
229
+ {
230
+ var invocationExpression = ( InvocationExpressionSyntax ) rewrittenNode ;
231
+ var oldArguments = invocationExpression . ArgumentList . Arguments ;
232
+ var newArguments = new SeparatedSyntaxList < ArgumentSyntax > ( ) . AddRange ( new [ ] { oldArguments [ 1 ] , oldArguments [ 0 ] } ) ;
233
+
234
+ return invocationExpression . WithArgumentList ( invocationExpression . ArgumentList . WithArguments ( newArguments ) ) ;
235
+ } ) ;
236
+ } ) ;
237
+ }
168
238
}
169
239
170
240
private static SyntaxTriviaList RemoveCompilerDirectives ( SyntaxTriviaList stl )
@@ -186,10 +256,11 @@ private static SyntaxTriviaList RemoveCompilerDirectives(SyntaxTriviaList stl)
186
256
187
257
class TransformationTracker
188
258
{
189
- Dictionary < SyntaxAnnotation , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , CompilationUnitSyntax > > _annotationToTransformation = new Dictionary < SyntaxAnnotation , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , CompilationUnitSyntax > > ( ) ;
259
+ Dictionary < SyntaxAnnotation , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , Dictionary < SyntaxNode , SyntaxNode > , CompilationUnitSyntax > > _annotationToTransformation = new Dictionary < SyntaxAnnotation , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , Dictionary < SyntaxNode , SyntaxNode > , CompilationUnitSyntax > > ( ) ;
190
260
Dictionary < SyntaxNode , List < SyntaxAnnotation > > _nodeToAnnotations = new Dictionary < SyntaxNode , List < SyntaxAnnotation > > ( ) ;
261
+ Dictionary < SyntaxAnnotation , SyntaxNode > _originalNodeLookup = new Dictionary < SyntaxAnnotation , SyntaxNode > ( ) ;
191
262
192
- public void AddTransformation ( IEnumerable < SyntaxNode > nodesToTransform , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , CompilationUnitSyntax > transformerFunc )
263
+ public void AddTransformation ( IEnumerable < SyntaxNode > nodesToTransform , Func < CompilationUnitSyntax , IEnumerable < SyntaxNode > , Dictionary < SyntaxNode , SyntaxNode > , CompilationUnitSyntax > transformerFunc )
193
264
{
194
265
var annotation = new SyntaxAnnotation ( ) ;
195
266
_annotationToTransformation [ annotation ] = transformerFunc ;
@@ -203,22 +274,39 @@ public void AddTransformation(IEnumerable<SyntaxNode> nodesToTransform, Func<Com
203
274
_nodeToAnnotations [ node ] = annotationsForNode ;
204
275
}
205
276
annotationsForNode . Add ( annotation ) ;
277
+
278
+ var originalNodeAnnotation = new SyntaxAnnotation ( ) ;
279
+ _originalNodeLookup [ originalNodeAnnotation ] = node ;
280
+ annotationsForNode . Add ( originalNodeAnnotation ) ;
206
281
}
207
282
}
208
283
209
284
public CompilationUnitSyntax TransformRoot ( CompilationUnitSyntax root )
210
285
{
211
286
root = root . ReplaceNodes ( _nodeToAnnotations . Keys , ( originalNode , rewrittenNode ) =>
212
287
{
213
- return rewrittenNode . WithAdditionalAnnotations ( _nodeToAnnotations [ originalNode ] ) ;
288
+ var ret = rewrittenNode . WithAdditionalAnnotations ( _nodeToAnnotations [ originalNode ] ) ;
289
+
290
+ return ret ;
214
291
} ) ;
215
292
216
293
foreach ( var kvp in _annotationToTransformation )
217
294
{
295
+ Dictionary < SyntaxNode , SyntaxNode > originalNodeMap = new Dictionary < SyntaxNode , SyntaxNode > ( ) ;
296
+ foreach ( var originalNodeKvp in _originalNodeLookup )
297
+ {
298
+ var annotatedNodes = root . GetAnnotatedNodes ( originalNodeKvp . Key ) . ToList ( ) ;
299
+ SyntaxNode annotatedNode = annotatedNodes . SingleOrDefault ( ) ;
300
+ if ( annotatedNode != null )
301
+ {
302
+ originalNodeMap [ annotatedNode ] = originalNodeKvp . Value ;
303
+ }
304
+ }
305
+
218
306
var syntaxAnnotation = kvp . Key ;
219
307
var transformation = kvp . Value ;
220
308
var nodesToTransform = root . GetAnnotatedNodes ( syntaxAnnotation ) ;
221
- root = transformation ( root , nodesToTransform ) ;
309
+ root = transformation ( root , nodesToTransform , originalNodeMap ) ;
222
310
}
223
311
224
312
return root ;
0 commit comments