Skip to content
This repository was archived by the owner on Jul 12, 2022. It is now read-only.

Commit f671697

Browse files
committed
Fix explicit visibility for partial types
The explicit visibility code now handles partial types correctly.
1 parent 4dd9b7f commit f671697

File tree

2 files changed

+116
-21
lines changed

2 files changed

+116
-21
lines changed

src/Microsoft.DotNet.CodeFormatting.Tests/Rules/ExplicitVisibilityRuleTests.cs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,5 +287,36 @@ internal class T
287287
}";
288288
Verify(text, expected);
289289
}
290+
291+
[Fact]
292+
public void LonePartialType()
293+
{
294+
var text = @"
295+
partial class C { }
296+
";
297+
298+
var expected = @"
299+
internal partial class C { }
300+
";
301+
302+
Verify(text, expected, runFormatter: false);
303+
}
304+
305+
306+
[Fact]
307+
public void CorrectPartialType()
308+
{
309+
var text = @"
310+
partial class C { }
311+
public partial class C { }
312+
";
313+
314+
var expected = @"
315+
public partial class C { }
316+
public partial class C { }
317+
";
318+
319+
Verify(text, expected, runFormatter: false);
320+
}
290321
}
291322
}

src/Microsoft.DotNet.CodeFormatting/Rules/ExplicitVisibilityRule.cs

Lines changed: 85 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,36 +19,41 @@ internal sealed class ExplicitVisibilityRule : IFormattingRule
1919
{
2020
private sealed class VisibilityRewriter : CSharpSyntaxRewriter
2121
{
22+
private readonly Document _document;
23+
private readonly CancellationToken _cancellationToken;
24+
private SemanticModel _semanticModel;
25+
26+
internal VisibilityRewriter(Document document, CancellationToken cancellationToken)
27+
{
28+
_document = document;
29+
_cancellationToken = cancellationToken;
30+
}
31+
2232
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
2333
{
2434
node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node);
25-
SyntaxKind visibilityKind = GetTypeDefaultVisibility(node);
26-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
35+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => GetTypeDefaultVisibility(node));
2736
}
2837

2938
public override SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
3039
{
31-
SyntaxKind visibilityKind = GetTypeDefaultVisibility(node);
32-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
40+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => GetTypeDefaultVisibility(node));
3341
}
3442

3543
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
3644
{
3745
node = (StructDeclarationSyntax)base.VisitStructDeclaration(node);
38-
SyntaxKind visibilityKind = GetTypeDefaultVisibility(node);
39-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
46+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => GetTypeDefaultVisibility(node));
4047
}
4148

4249
public override SyntaxNode VisitDelegateDeclaration(DelegateDeclarationSyntax node)
4350
{
44-
SyntaxKind visibilityKind = GetTypeDefaultVisibility(node);
45-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
51+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => GetDelegateTypeDefaultVisibility(node));
4652
}
4753

4854
public override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
4955
{
50-
SyntaxKind visibilityKind = GetTypeDefaultVisibility(node);
51-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
56+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => GetTypeDefaultVisibility(node));
5257
}
5358

5459
public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
@@ -58,7 +63,7 @@ public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyn
5863
return node;
5964
}
6065

61-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
66+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
6267
}
6368

6469
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
@@ -68,7 +73,7 @@ public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
6873
return node;
6974
}
7075

71-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
76+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
7277
}
7378

7479
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
@@ -78,7 +83,7 @@ public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax no
7883
return node;
7984
}
8085

81-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
86+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
8287
}
8388

8489
public override SyntaxNode VisitEventDeclaration(EventDeclarationSyntax node)
@@ -88,24 +93,82 @@ public override SyntaxNode VisitEventDeclaration(EventDeclarationSyntax node)
8893
return node;
8994
}
9095

91-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
96+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
9297
}
9398

9499
public override SyntaxNode VisitEventFieldDeclaration(EventFieldDeclarationSyntax node)
95100
{
96-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
101+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
97102
}
98103

99104
public override SyntaxNode VisitFieldDeclaration(FieldDeclarationSyntax node)
100105
{
101-
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
106+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), () => SyntaxKind.PrivateKeyword);
107+
}
108+
109+
private SyntaxKind GetTypeDefaultVisibility(BaseTypeDeclarationSyntax node)
110+
{
111+
// In the case of partial types we need to use the existing visibility if it exists
112+
if (node.Modifiers.Any(x => x.CSharpContextualKind() == SyntaxKind.PartialKeyword))
113+
{
114+
SyntaxKind? kind = GetExistingPartialVisibility(node);
115+
if (kind.HasValue)
116+
{
117+
return kind.Value;
118+
}
119+
}
120+
121+
return GetDelegateTypeDefaultVisibility(node);
102122
}
103123

104-
private static SyntaxKind GetTypeDefaultVisibility(SyntaxNode node)
124+
private SyntaxKind GetDelegateTypeDefaultVisibility(SyntaxNode node)
105125
{
106126
return IsNestedDeclaration(node) ? SyntaxKind.PrivateKeyword : SyntaxKind.InternalKeyword;
107127
}
108128

129+
private SyntaxKind? GetExistingPartialVisibility(BaseTypeDeclarationSyntax declarationSyntax)
130+
{
131+
// Getting the SemanticModel is a relatively expensive operation. Can take a few seconds in
132+
// projects of significant size. It is delay created to avoid this in files which already
133+
// conform to the standards.
134+
if (_semanticModel == null)
135+
{
136+
_semanticModel = _document.GetSemanticModelAsync(_cancellationToken).Result;
137+
}
138+
139+
var symbol = _semanticModel.GetDeclaredSymbol(declarationSyntax, _cancellationToken);
140+
if (symbol == null)
141+
{
142+
return null;
143+
}
144+
145+
switch (symbol.DeclaredAccessibility)
146+
{
147+
case Accessibility.Friend:
148+
return SyntaxKind.InternalKeyword;
149+
case Accessibility.Public:
150+
return SyntaxKind.PublicKeyword;
151+
case Accessibility.Private:
152+
return SyntaxKind.PrivateKeyword;
153+
case Accessibility.Protected:
154+
return SyntaxKind.ProtectedKeyword;
155+
default: return null;
156+
}
157+
}
158+
159+
private static SyntaxKind? GetVisibilityModifier(SyntaxTokenList list)
160+
{
161+
foreach (var token in list)
162+
{
163+
if (SyntaxFacts.IsAccessibilityModifier(token.CSharpKind()))
164+
{
165+
return token.CSharpKind();
166+
}
167+
}
168+
169+
return null;
170+
}
171+
109172
private static bool IsNestedDeclaration(SyntaxNode node)
110173
{
111174
var current = node.Parent;
@@ -126,15 +189,16 @@ private static bool IsNestedDeclaration(SyntaxNode node)
126189
/// Return a node declaration that has a visibility modifier. If one isn't present it will be added as the
127190
/// first modifier. Any trivia before the node will be added as leading trivia to the added <see cref="SyntaxToken"/>.
128191
/// </summary>
129-
private static MemberDeclarationSyntax EnsureVisibility<T>(T node, SyntaxTokenList originalModifiers, Func<T, SyntaxTokenList, T> withModifiers, SyntaxKind visibilityKind) where T : MemberDeclarationSyntax
192+
private static MemberDeclarationSyntax EnsureVisibility<T>(T node, SyntaxTokenList originalModifiers, Func<T, SyntaxTokenList, T> withModifiers, Func<SyntaxKind> getDefaultVisibility) where T : MemberDeclarationSyntax
130193
{
131-
Debug.Assert(SyntaxFacts.IsAccessibilityModifier(visibilityKind));
132-
133194
if (originalModifiers.Any(x => SyntaxFacts.IsAccessibilityModifier(x.CSharpKind())))
134195
{
135196
return node;
136197
}
137198

199+
SyntaxKind visibilityKind = getDefaultVisibility();
200+
Debug.Assert(SyntaxFacts.IsAccessibilityModifier(visibilityKind));
201+
138202
SyntaxTokenList modifierList;
139203
if (originalModifiers.Count == 0)
140204
{
@@ -179,7 +243,7 @@ public async Task<Document> ProcessAsync(Document document, CancellationToken ca
179243
return document;
180244
}
181245

182-
var rewriter = new VisibilityRewriter();
246+
var rewriter = new VisibilityRewriter(document, cancellationToken);
183247
var newNode = rewriter.Visit(syntaxNode);
184248
return document.WithSyntaxRoot(newNode);
185249
}

0 commit comments

Comments
 (0)