@@ -59,6 +59,30 @@ public static IEnumerable<object[]> InvalidDeclarations
5959 }
6060 }
6161
62+ public static IEnumerable < object [ ] > ValidNestedDeclarations
63+ {
64+ get
65+ {
66+ yield return new object [ ] { "public" , "class" } ;
67+ yield return new object [ ] { "protected" , "class" } ;
68+ yield return new object [ ] { "internal" , "class" } ;
69+ yield return new object [ ] { "protected internal" , "class" } ;
70+ yield return new object [ ] { "private" , "class" } ;
71+
72+ yield return new object [ ] { "public" , "struct" } ;
73+ yield return new object [ ] { "protected" , "struct" } ;
74+ yield return new object [ ] { "internal" , "struct" } ;
75+ yield return new object [ ] { "protected internal" , "struct" } ;
76+ yield return new object [ ] { "private" , "struct" } ;
77+
78+ yield return new object [ ] { "public" , "interface" } ;
79+ yield return new object [ ] { "protected" , "interface" } ;
80+ yield return new object [ ] { "internal" , "interface" } ;
81+ yield return new object [ ] { "protected internal" , "interface" } ;
82+ yield return new object [ ] { "private" , "interface" } ;
83+ }
84+ }
85+
6286 /// <summary>
6387 /// Verifies that a valid declaration (with an access modifier or not a partial type) will not produce a diagnostic.
6488 /// </summary>
@@ -86,6 +110,7 @@ public async Task TestInvalidDeclarationAsync(string declaration)
86110
87111 await this . VerifyCSharpDiagnosticAsync ( testCode , this . CSharpDiagnostic ( ) . WithLocation ( 1 , 2 + declaration . Length ) , CancellationToken . None ) . ConfigureAwait ( false ) ;
88112 await this . VerifyCSharpDiagnosticAsync ( fixedTestCode , EmptyDiagnosticResults , CancellationToken . None ) . ConfigureAwait ( false ) ;
113+ await this . VerifyCSharpFixAsync ( testCode , fixedTestCode ) . ConfigureAwait ( false ) ;
89114 }
90115
91116 /// <summary>
@@ -170,34 +195,94 @@ public partial class Foo
170195 /// <param name="typeKeyword">The type keyword to use.</param>
171196 /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
172197 [ Theory ]
173- [ InlineData ( "public" , "class" ) ]
174- [ InlineData ( "protected" , "class" ) ]
175- [ InlineData ( "internal" , "class" ) ]
176- [ InlineData ( "protected internal" , "class" ) ]
177- [ InlineData ( "private" , "class" ) ]
178- [ InlineData ( "public" , "struct" ) ]
179- [ InlineData ( "protected" , "struct" ) ]
180- [ InlineData ( "internal" , "struct" ) ]
181- [ InlineData ( "protected internal" , "struct" ) ]
182- [ InlineData ( "private" , "struct" ) ]
198+ [ MemberData ( nameof ( ValidNestedDeclarations ) ) ]
183199 public async Task TestNestedTypeAccessModifiersAsync ( string accessModifier , string typeKeyword )
184200 {
185201 var testCode = $@ "
186202internal static partial class TestPartial
187203{{
188204 { accessModifier } partial { typeKeyword } PartialInner
189205 {{
190- public int Do()
191- {{
192- return 2;
193- }}
194206 }}
195207}}
196208" ;
197209
198210 await this . VerifyCSharpDiagnosticAsync ( testCode , EmptyDiagnosticResults , CancellationToken . None ) . ConfigureAwait ( false ) ;
199211 }
200212
213+ /// <summary>
214+ /// Verifies that a nested type without access modifiers will produce a diagnostic and can be fixed correctly.
215+ /// </summary>
216+ /// <param name="declaration">The declaration to verify.</param>
217+ /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
218+ [ Theory ]
219+ [ MemberData ( nameof ( InvalidDeclarations ) ) ]
220+ public async Task TestNestedTypeWithoutAccessModifierAsync ( string declaration )
221+ {
222+ var testCode = $@ "
223+ public class Foo
224+ {{
225+ { declaration } Bar
226+ {{
227+ }}
228+ }}
229+ " ;
230+
231+ var fixedTestCode = $@ "
232+ public class Foo
233+ {{
234+ private { declaration } Bar
235+ {{
236+ }}
237+ }}
238+ " ;
239+
240+ await this . VerifyCSharpDiagnosticAsync ( testCode , this . CSharpDiagnostic ( ) . WithLocation ( 4 , 6 + declaration . Length ) , CancellationToken . None ) . ConfigureAwait ( false ) ;
241+ await this . VerifyCSharpDiagnosticAsync ( fixedTestCode , EmptyDiagnosticResults , CancellationToken . None ) . ConfigureAwait ( false ) ;
242+ await this . VerifyCSharpFixAsync ( testCode , fixedTestCode ) . ConfigureAwait ( false ) ;
243+ }
244+
245+ /// <summary>
246+ /// Verifies that the code fix will properly copy over the access modifier defined in another fragment of the nested partial element.
247+ /// </summary>
248+ /// <param name="accessModifier">The access modifier to use for the nested type.</param>
249+ /// <param name="typeKeyword">The type keyword to use.</param>
250+ /// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
251+ [ Theory ]
252+ [ MemberData ( nameof ( ValidNestedDeclarations ) ) ]
253+ public async Task TestProperNestedAccessModifierPropagationAsync ( string accessModifier , string typeKeyword )
254+ {
255+ var testCode = $@ "
256+ public class Foo
257+ {{
258+ { accessModifier } partial { typeKeyword } Bar
259+ {{
260+ }}
261+
262+ partial { typeKeyword } Bar
263+ {{
264+ }}
265+ }}
266+ " ;
267+
268+ var fixedTestCode = $@ "
269+ public class Foo
270+ {{
271+ { accessModifier } partial { typeKeyword } Bar
272+ {{
273+ }}
274+
275+ { accessModifier } partial { typeKeyword } Bar
276+ {{
277+ }}
278+ }}
279+ " ;
280+
281+ await this . VerifyCSharpDiagnosticAsync ( testCode , this . CSharpDiagnostic ( ) . WithLocation ( 8 , 14 + typeKeyword . Length ) , CancellationToken . None ) . ConfigureAwait ( false ) ;
282+ await this . VerifyCSharpDiagnosticAsync ( fixedTestCode , EmptyDiagnosticResults , CancellationToken . None ) . ConfigureAwait ( false ) ;
283+ await this . VerifyCSharpFixAsync ( testCode , fixedTestCode ) . ConfigureAwait ( false ) ;
284+ }
285+
201286 /// <inheritdoc/>
202287 protected override IEnumerable < DiagnosticAnalyzer > GetCSharpDiagnosticAnalyzers ( )
203288 {
0 commit comments