@@ -19,32 +19,34 @@ namespace WpfAnalyzers;
1919[ Shared ]
2020internal class ImplementValueConverterFix : DocumentEditorCodeFixProvider
2121{
22- private static readonly MethodDeclarationSyntax IMultiValueConverterConvert = ParseMethod (
23- @" public object Convert(object[] values, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
24- {
25- throw new System.NotImplementedException();
26- }" ) ;
27-
2822 public override ImmutableArray < string > FixableDiagnosticIds { get ; } = ImmutableArray . Create ( "CS0535" ) ;
2923
3024 protected override async Task RegisterCodeFixesAsync ( DocumentEditorCodeFixContext context )
3125 {
3226 var document = context . Document ;
3327 var syntaxRoot = await document . GetSyntaxRootAsync ( context . CancellationToken )
3428 . ConfigureAwait ( false ) ;
29+
30+ var semanticModel = await document . GetSemanticModelAsync ( context . CancellationToken )
31+ . ConfigureAwait ( false ) ;
3532 foreach ( var diagnostic in context . Diagnostics )
3633 {
3734 if ( syntaxRoot is { } &&
35+ semanticModel is { } &&
3836 syntaxRoot . TryFindNodeOrAncestor ( diagnostic , out ClassDeclarationSyntax ? classDeclaration ) )
3937 {
38+ var nullableContext = semanticModel . GetNullableContext ( diagnostic . Location . SourceSpan . Start ) ;
4039 if ( HasInterface ( classDeclaration , KnownSymbols . IValueConverter ) )
4140 {
4241 if ( diagnostic . GetMessage ( CultureInfo . InvariantCulture )
4342 . Contains ( "does not implement interface member 'IValueConverter.Convert(object, Type, object, CultureInfo)'" ) )
4443 {
4544 context . RegisterCodeFix (
4645 "Implement IValueConverter.Convert for one way bindings." ,
47- ( editor , _ ) => editor . AddMethod ( classDeclaration , Convert ( editor . Generator ) ) ,
46+ ( editor , _ ) =>
47+ {
48+ editor . AddMethod ( classDeclaration , IValueConverter . Convert ( editor . Generator , nullableContext ) ) ;
49+ } ,
4850 "Implement IValueConverter" ,
4951 diagnostic ) ;
5052 }
@@ -54,7 +56,7 @@ protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContex
5456 {
5557 context . RegisterCodeFix (
5658 "Implement IValueConverter.ConvertBack for one way bindings." ,
57- ( editor , _ ) => editor . AddMethod ( classDeclaration , ConvertBack ( editor . Generator , classDeclaration . Identifier . ValueText ) ) ,
59+ ( editor , _ ) => editor . AddMethod ( classDeclaration , IValueConverter . ConvertBack ( editor . Generator , classDeclaration . Identifier . ValueText , nullableContext ) ) ,
5860 "Implement IValueConverter" ,
5961 diagnostic ) ;
6062 }
@@ -67,7 +69,7 @@ protected override async Task RegisterCodeFixesAsync(DocumentEditorCodeFixContex
6769 {
6870 context . RegisterCodeFix (
6971 "Implement IMultiValueConverter.Convert for one way bindings." ,
70- ( editor , _ ) => editor . AddMethod ( classDeclaration , IMultiValueConverterConvert ) ,
72+ ( editor , _ ) => editor . AddMethod ( classDeclaration , IMultiValueConverter . Convert ( editor . Generator , nullableContext ) ) ,
7173 "Implement IMultiValueConverter" ,
7274 diagnostic ) ;
7375 }
@@ -106,48 +108,77 @@ private static bool HasInterface(ClassDeclarationSyntax classDeclaration, Qualif
106108 return false ;
107109 }
108110
109- private static MethodDeclarationSyntax Convert ( SyntaxGenerator generator )
111+ private static MethodDeclarationSyntax IMultiValueConverterConvertBack ( string containingTypeName )
110112 {
111- return ( MethodDeclarationSyntax ) generator . MethodDeclaration (
112- accessibility : Accessibility . Public ,
113- returnType : SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ,
114- name : "Convert" ,
115- parameters : new [ ]
116- {
117- generator . ParameterDeclaration ( "value" , SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ) ,
118- generator . ParameterDeclaration ( "targetType" , SyntaxFactory . ParseTypeName ( "System.Type" ) . WithSimplifiedNames ( ) ) ,
119- generator . ParameterDeclaration ( "parameter" , SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ) ,
120- generator . ParameterDeclaration ( "culture" , SyntaxFactory . ParseTypeName ( "System.Globalization.CultureInfo" ) . WithSimplifiedNames ( ) ) ,
121- } ,
122- statements : new [ ] { generator . ThrowStatement ( generator . ObjectCreationExpression ( SyntaxFactory . ParseTypeName ( "System.NotImplementedException" ) . WithSimplifiedNames ( ) ) ) } ) ;
113+ var code = StringBuilderPool . Borrow ( )
114+ . AppendLine ( " object[] System.Windows.Data.IMultiValueConverter.ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)" )
115+ . AppendLine ( " {" )
116+ . AppendLine ( $ " throw new System.NotSupportedException($\" {{nameof({ containingTypeName } )}} can only be used in OneWay bindings\" );")
117+ . AppendLine ( " }" )
118+ . Return ( ) ;
119+ return ParseMethod ( code ) ;
123120 }
124121
125- private static MethodDeclarationSyntax ConvertBack ( SyntaxGenerator generator , string containingTypeName )
122+ private static TypeSyntax ParseTypeName ( string text ) => SyntaxFactory . ParseTypeName ( text ) . WithSimplifiedNames ( ) ;
123+
124+ private static TypeSyntax Object ( NullableContext nullableContext ) => nullableContext switch
126125 {
127- return ( ( MethodDeclarationSyntax ) generator . MethodDeclaration (
128- returnType : SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ,
129- name : "ConvertBack" ,
130- parameters : new [ ]
131- {
132- generator . ParameterDeclaration ( "value" , SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ) ,
133- generator . ParameterDeclaration ( "targetType" , SyntaxFactory . ParseTypeName ( "System.Type" ) . WithSimplifiedNames ( ) ) ,
134- generator . ParameterDeclaration ( "parameter" , SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ) ,
135- generator . ParameterDeclaration ( "culture" , SyntaxFactory . ParseTypeName ( "System.Globalization.CultureInfo" ) . WithSimplifiedNames ( ) ) ,
136- } ,
137- statements : new [ ] { Throw ( ) } ) )
138- . WithExplicitInterfaceSpecifier ( SyntaxFactory . ExplicitInterfaceSpecifier ( SyntaxFactory . ParseName ( "IValueConverter" ) ) ) ;
139-
140- ThrowStatementSyntax Throw ( )
126+ NullableContext . WarningsEnabled => SyntaxFactory . NullableType ( SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ) ,
127+ _ => SyntaxFactory . PredefinedType ( SyntaxFactory . Token ( SyntaxKind . ObjectKeyword ) ) ,
128+ } ;
129+
130+ private static MethodDeclarationSyntax ParseMethod ( string code )
131+ {
132+ return Parse . MethodDeclaration ( code )
133+ . WithSimplifiedNames ( )
134+ . WithLeadingTrivia ( SyntaxFactory . ElasticMarker )
135+ . WithTrailingTrivia ( SyntaxFactory . ElasticMarker ) ;
136+ }
137+
138+ private static class IValueConverter
139+ {
140+ internal static MethodDeclarationSyntax Convert ( SyntaxGenerator generator , NullableContext nullableContext )
141141 {
142- return ( ThrowStatementSyntax ) generator . ThrowStatement (
143- generator . ObjectCreationExpression (
144- SyntaxFactory . ParseTypeName ( "System.NotSupportedException" ) ,
145- SyntaxFactory . Argument (
146- expression : SyntaxFactory . InterpolatedStringExpression (
147- stringStartToken : SyntaxFactory . Token ( SyntaxKind . InterpolatedStringStartToken ) ,
148- contents : SyntaxFactory . List (
149- new InterpolatedStringContentSyntax [ ]
150- {
142+ return ( MethodDeclarationSyntax ) generator . MethodDeclaration (
143+ accessibility : Accessibility . Public ,
144+ returnType : Object ( nullableContext ) ,
145+ name : "Convert" ,
146+ parameters : new [ ]
147+ {
148+ generator . ParameterDeclaration ( "value" , Object ( nullableContext ) ) ,
149+ generator . ParameterDeclaration ( "targetType" , ParseTypeName ( "System.Type" ) ) ,
150+ generator . ParameterDeclaration ( "parameter" , Object ( nullableContext ) ) ,
151+ generator . ParameterDeclaration ( "culture" , ParseTypeName ( "System.Globalization.CultureInfo" ) ) ,
152+ } ,
153+ statements : new [ ] { generator . ThrowStatement ( generator . ObjectCreationExpression ( ParseTypeName ( "System.NotImplementedException" ) ) ) } ) ;
154+ }
155+
156+ internal static MethodDeclarationSyntax ConvertBack ( SyntaxGenerator generator , string containingTypeName , NullableContext nullableContext )
157+ {
158+ return ( ( MethodDeclarationSyntax ) generator . MethodDeclaration (
159+ returnType : Object ( nullableContext ) ,
160+ name : "ConvertBack" ,
161+ parameters : new [ ]
162+ {
163+ generator . ParameterDeclaration ( "value" , Object ( nullableContext ) ) ,
164+ generator . ParameterDeclaration ( "targetType" , ParseTypeName ( "System.Type" ) ) ,
165+ generator . ParameterDeclaration ( "parameter" , Object ( nullableContext ) ) ,
166+ generator . ParameterDeclaration ( "culture" , ParseTypeName ( "System.Globalization.CultureInfo" ) ) ,
167+ } ,
168+ statements : new [ ] { Throw ( ) } ) )
169+ . WithExplicitInterfaceSpecifier ( SyntaxFactory . ExplicitInterfaceSpecifier ( SyntaxFactory . ParseName ( "IValueConverter" ) ) ) ;
170+
171+ ThrowStatementSyntax Throw ( )
172+ {
173+ return ( ThrowStatementSyntax ) generator . ThrowStatement (
174+ generator . ObjectCreationExpression (
175+ ParseTypeName ( "System.NotSupportedException" ) ,
176+ SyntaxFactory . Argument (
177+ expression : SyntaxFactory . InterpolatedStringExpression (
178+ stringStartToken : SyntaxFactory . Token ( SyntaxKind . InterpolatedStringStartToken ) ,
179+ contents : SyntaxFactory . List (
180+ new InterpolatedStringContentSyntax [ ]
181+ {
151182 SyntaxFactory . Interpolation (
152183 openBraceToken : SyntaxFactory . Token ( SyntaxKind . OpenBraceToken ) ,
153184 expression : ( ExpressionSyntax ) generator . NameOfExpression ( SyntaxFactory . ParseTypeName ( containingTypeName ) ) ,
@@ -161,27 +192,28 @@ ThrowStatementSyntax Throw()
161192 text : " can only be used in OneWay bindings" ,
162193 valueText : " can only be used in OneWay bindings" ,
163194 trailing : default ) ) ,
164- } ) ,
165- stringEndToken : SyntaxFactory . Token ( SyntaxKind . InterpolatedStringEndToken ) ) ) ) ) ;
195+ } ) ,
196+ stringEndToken : SyntaxFactory . Token ( SyntaxKind . InterpolatedStringEndToken ) ) ) ) ) ;
197+ }
166198 }
167199 }
168200
169- private static MethodDeclarationSyntax IMultiValueConverterConvertBack ( string containingTypeName )
201+ private static class IMultiValueConverter
170202 {
171- var code = StringBuilderPool . Borrow ( )
172- . AppendLine ( " object[] System.Windows.Data.IMultiValueConverter.ConvertBack(object value, System.Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)" )
173- . AppendLine ( " {" )
174- . AppendLine ( $ " throw new System.NotSupportedException($ \" {{nameof( { containingTypeName } )}} can only be used in OneWay bindings \" );" )
175- . AppendLine ( " }" )
176- . Return ( ) ;
177- return ParseMethod ( code ) ;
178- }
179-
180- private static MethodDeclarationSyntax ParseMethod ( string code )
181- {
182- return Parse . MethodDeclaration ( code )
183- . WithSimplifiedNames ( )
184- . WithLeadingTrivia ( SyntaxFactory . ElasticMarker )
185- . WithTrailingTrivia ( SyntaxFactory . ElasticMarker ) ;
203+ internal static MethodDeclarationSyntax Convert ( SyntaxGenerator generator , NullableContext nullableContext )
204+ {
205+ return ( MethodDeclarationSyntax ) generator . MethodDeclaration (
206+ accessibility : Accessibility . Public ,
207+ returnType : Object ( nullableContext ) ,
208+ name : "Convert" ,
209+ parameters : new [ ]
210+ {
211+ generator . ParameterDeclaration ( "value" , SyntaxFactory . ArrayType ( Object ( nullableContext ) ) ) ,
212+ generator . ParameterDeclaration ( "targetType" , ParseTypeName ( "System.Type" ) ) ,
213+ generator . ParameterDeclaration ( "parameter" , Object ( nullableContext ) ) ,
214+ generator . ParameterDeclaration ( "culture" , ParseTypeName ( "System.Globalization.CultureInfo" ) ) ,
215+ } ,
216+ statements : new [ ] { generator . ThrowStatement ( generator . ObjectCreationExpression ( ParseTypeName ( "System.NotImplementedException" ) ) ) } ) ;
217+ }
186218 }
187219}
0 commit comments