1414namespace Philips . CodeAnalysis . MoqAnalyzers
1515{
1616 [ ExportCodeFixProvider ( LanguageNames . CSharp , Name = nameof ( MockDisposableClassesShouldSetupDisposeCodeFixProvider ) ) , Shared ]
17- public class MockDisposableClassesShouldSetupDisposeCodeFixProvider : SingleDiagnosticCodeFixProvider < ObjectCreationExpressionSyntax >
17+ public class MockDisposableClassesShouldSetupDisposeCodeFixProvider : SingleDiagnosticCodeFixProvider < ExpressionSyntax >
1818 {
1919 protected override string Title => "Use configured disposable mock type" ;
2020
2121 protected override DiagnosticId DiagnosticId => DiagnosticId . MockDisposableObjectsShouldSetupDispose ;
2222
23- protected override async Task < Document > ApplyFix ( Document document , ObjectCreationExpressionSyntax node , ImmutableDictionary < string , string > properties , CancellationToken cancellationToken )
23+ protected override async Task < Document > ApplyFix ( Document document , ExpressionSyntax node , ImmutableDictionary < string , string > properties , CancellationToken cancellationToken )
2424 {
2525 var configuredTypeName = GetPreferredDisposableMockType ( properties ) ;
2626 if ( string . IsNullOrWhiteSpace ( configuredTypeName ) )
@@ -34,55 +34,98 @@ protected override async Task<Document> ApplyFix(Document document, ObjectCreati
3434 return document ;
3535 }
3636
37- if ( node . Type is not GenericNameSyntax objectCreationGenericName )
37+ ExpressionSyntax currentNode = FindCurrentNode < ExpressionSyntax > ( rootNode , node ) ;
38+ if ( currentNode == null )
3839 {
3940 return document ;
4041 }
4142
42- TypeSyntax replacementTypeSyntax = CreateReplacementTypeSyntax ( configuredTypeName , objectCreationGenericName . TypeArgumentList ) ;
43+ TypeSyntax declaredTypeToReplace = GetDeclaredTypeFromContext ( currentNode ) ;
4344
44- TypeSyntax replacementType = replacementTypeSyntax . WithAdditionalAnnotations ( Formatter . Annotation ) ;
45+ if ( currentNode is ImplicitObjectCreationExpressionSyntax )
46+ {
47+ if ( declaredTypeToReplace is not GenericNameSyntax declaredGenericName )
48+ {
49+ return document ;
50+ }
51+
52+ TypeSyntax replacementType = CreateReplacementTypeSyntax ( configuredTypeName , declaredGenericName . TypeArgumentList )
53+ . WithAdditionalAnnotations ( Formatter . Annotation ) ;
4554
46- SyntaxNode newRoot ;
47- TypeSyntax declaredTypeToReplace = GetDeclaredTypeToReplace ( node , objectCreationGenericName ) ;
55+ SyntaxNode newRoot = rootNode . ReplaceNode (
56+ declaredTypeToReplace ,
57+ replacementType . WithTriviaFrom ( declaredTypeToReplace ) ) ;
58+
59+ return document . WithSyntaxRoot ( newRoot ) ;
60+ }
4861
49- if ( declaredTypeToReplace != null )
62+ if ( currentNode is ObjectCreationExpressionSyntax explicitObjectCreation &&
63+ explicitObjectCreation . Type is GenericNameSyntax explicitGenericName )
5064 {
51- newRoot = rootNode . ReplaceNodes (
52- new SyntaxNode [ ] { node . Type , declaredTypeToReplace } ,
53- ( original , _ ) => replacementType . WithTriviaFrom ( original ) ) ;
65+ TypeSyntax replacementType = CreateReplacementTypeSyntax ( configuredTypeName , explicitGenericName . TypeArgumentList )
66+ . WithAdditionalAnnotations ( Formatter . Annotation ) ;
67+
68+ SyntaxNode newRoot ;
69+ if ( IsMatchingMockDeclaredType ( declaredTypeToReplace , explicitGenericName ) )
70+ {
71+ newRoot = rootNode . ReplaceNodes (
72+ new SyntaxNode [ ] { explicitObjectCreation . Type , declaredTypeToReplace } ,
73+ ( original , _ ) => replacementType . WithTriviaFrom ( original ) ) ;
74+ }
75+ else
76+ {
77+ newRoot = rootNode . ReplaceNode (
78+ explicitObjectCreation . Type ,
79+ replacementType . WithTriviaFrom ( explicitObjectCreation . Type ) ) ;
80+ }
81+
82+ return document . WithSyntaxRoot ( newRoot ) ;
5483 }
55- else
84+
85+ return document ;
86+ }
87+
88+ private static TNode FindCurrentNode < TNode > ( SyntaxNode rootNode , SyntaxNode originalNode )
89+ where TNode : SyntaxNode
90+ {
91+ if ( rootNode == null || originalNode == null )
5692 {
57- newRoot = rootNode . ReplaceNode (
58- node . Type ,
59- replacementType . WithTriviaFrom ( node . Type ) ) ;
93+ return null ;
6094 }
6195
62- return document . WithSyntaxRoot ( newRoot ) ;
96+ SyntaxNode currentNode = rootNode . FindNode ( originalNode . Span , getInnermostNodeForTie : true ) ;
97+ return currentNode as TNode ;
6398 }
6499
65- private static TypeSyntax GetDeclaredTypeToReplace ( ObjectCreationExpressionSyntax objectCreationSyntax , GenericNameSyntax originalObjectCreationType )
100+ private static TypeSyntax GetDeclaredTypeFromContext ( SyntaxNode node )
66101 {
67- VariableDeclarationSyntax variableDeclarationSyntax = objectCreationSyntax . FirstAncestorOrSelf < VariableDeclarationSyntax > ( ) ;
68- if ( variableDeclarationSyntax != null &&
69- variableDeclarationSyntax . Type is GenericNameSyntax declaredGenericName &&
70- declaredGenericName . Identifier . ValueText == originalObjectCreationType . Identifier . ValueText )
102+ VariableDeclarationSyntax variableDeclarationSyntax = node . FirstAncestorOrSelf < VariableDeclarationSyntax > ( ) ;
103+ if ( variableDeclarationSyntax != null )
71104 {
72105 return variableDeclarationSyntax . Type ;
73106 }
74107
75- PropertyDeclarationSyntax propertyDeclarationSyntax = objectCreationSyntax . FirstAncestorOrSelf < PropertyDeclarationSyntax > ( ) ;
76- if ( propertyDeclarationSyntax != null &&
77- propertyDeclarationSyntax . Type is GenericNameSyntax propertyGenericName &&
78- propertyGenericName . Identifier . ValueText == originalObjectCreationType . Identifier . ValueText )
108+ PropertyDeclarationSyntax propertyDeclarationSyntax = node . FirstAncestorOrSelf < PropertyDeclarationSyntax > ( ) ;
109+ if ( propertyDeclarationSyntax != null )
79110 {
80111 return propertyDeclarationSyntax . Type ;
81112 }
82113
114+ FieldDeclarationSyntax fieldDeclarationSyntax = node . FirstAncestorOrSelf < FieldDeclarationSyntax > ( ) ;
115+ if ( fieldDeclarationSyntax != null )
116+ {
117+ return fieldDeclarationSyntax . Declaration ? . Type ;
118+ }
119+
83120 return null ;
84121 }
85122
123+ private static bool IsMatchingMockDeclaredType ( TypeSyntax declaredTypeSyntax , GenericNameSyntax originalObjectCreationType )
124+ {
125+ return declaredTypeSyntax is GenericNameSyntax declaredGenericName &&
126+ declaredGenericName . Identifier . ValueText == originalObjectCreationType . Identifier . ValueText ;
127+ }
128+
86129 private static string GetPreferredDisposableMockType ( ImmutableDictionary < string , string > properties )
87130 {
88131 if ( properties != null &&
0 commit comments