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

Commit a89aa10

Browse files
committed
Initial pass at explicit visibility rule
This rule adds explicit visibility enforcement. Right now it supports types and method declarations. Need to expand out into other members (properties, events, etc ...).
1 parent fdfc103 commit a89aa10

File tree

4 files changed

+255
-1
lines changed

4 files changed

+255
-1
lines changed

src/Microsoft.DotNet.CodeFormatting.Tests/Microsoft.DotNet.CodeFormatting.Tests.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
</ItemGroup>
106106
<ItemGroup>
107107
<Compile Include="CodeFormattingTestBase.cs" />
108+
<Compile Include="Rules\ExplicitVisibilityRuleTests.cs" />
108109
<Compile Include="Rules\HasNewLineBeforeFirstNamespaceFormattingRuleTests.cs" />
109110
<Compile Include="Rules\HasNoIllegalHeadersFormattingRuleTests.cs" />
110111
<Compile Include="Rules\HasNewLineBeforeFirstUsingFormattingRuleTests.cs" />
@@ -122,7 +123,9 @@
122123
<Link>IllegalHeaders.md</Link>
123124
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
124125
</None>
125-
<None Include="packages.config" />
126+
<None Include="packages.config">
127+
<SubType>Designer</SubType>
128+
</None>
126129
</ItemGroup>
127130
<ItemGroup>
128131
<ProjectReference Include="..\Microsoft.DotNet.CodeFormatting\Microsoft.DotNet.CodeFormatting.csproj">
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Text;
5+
using System.Threading.Tasks;
6+
using Xunit;
7+
8+
namespace Microsoft.DotNet.CodeFormatting.Tests
9+
{
10+
public sealed class ExplicitVisibilityRuleTests : CodeFormattingTestBase
11+
{
12+
internal override IFormattingRule GetFormattingRule()
13+
{
14+
return new Rules.ExplicitVisibilityRule();
15+
}
16+
17+
[Fact]
18+
public void TestTypeVisibility()
19+
{
20+
var text = @"
21+
class C1 { }
22+
sealed partial class C2 { }
23+
struct S1 { }
24+
interface I1 { }
25+
enum E1 { }
26+
delegate void D1() { }
27+
";
28+
29+
var expected = @"
30+
internal class C1 { }
31+
internal sealed partial class C2 { }
32+
internal struct S1 { }
33+
internal interface I1 { }
34+
internal enum E1 { }
35+
internal delegate void D1() { }
36+
";
37+
38+
Verify(text, expected, runFormatter: false);
39+
}
40+
41+
[Fact]
42+
public void TestNestedTypeVisibility()
43+
{
44+
var text = @"
45+
class C
46+
{
47+
class C { }
48+
struct S { }
49+
interface I { }
50+
enum E { }
51+
delegate void D() { }
52+
}
53+
";
54+
55+
var expected = @"
56+
internal class C
57+
{
58+
private class C { }
59+
private struct S { }
60+
private interface I { }
61+
private enum E { }
62+
private delegate void D() { }
63+
}
64+
";
65+
66+
Verify(text, expected, runFormatter: false);
67+
}
68+
69+
[Fact]
70+
public void TestMethodVisibility()
71+
{
72+
var text = @"
73+
internal class C
74+
{
75+
void M1();
76+
internal void M2();
77+
}
78+
79+
internal struct S
80+
{
81+
void M1();
82+
internal void M2();
83+
}
84+
85+
internal interface I
86+
{
87+
void M1();
88+
void M2();
89+
}
90+
";
91+
92+
var expected = @"
93+
internal class C
94+
{
95+
private void M1();
96+
internal void M2();
97+
}
98+
99+
internal struct S
100+
{
101+
private void M1();
102+
internal void M2();
103+
}
104+
105+
internal interface I
106+
{
107+
void M1();
108+
void M2();
109+
}
110+
";
111+
112+
Verify(text, expected, runFormatter: false);
113+
}
114+
}
115+
}

src/Microsoft.DotNet.CodeFormatting/Microsoft.DotNet.CodeFormatting.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
<Compile Include="RuleOrderAttribute.cs" />
8888
<Compile Include="Properties\AssemblyInfo.cs" />
8989
<Compile Include="IOrderMetadata.cs" />
90+
<Compile Include="Rules\ExplicitVisibilityRule.cs" />
9091
<Compile Include="Rules\HasCopyrightHeaderFormattingRule.cs" />
9192
<Compile Include="Rules\HasNewLineBeforeFirstNamespaceFormattingRule.cs" />
9293
<Compile Include="Rules\HasNewLineBeforeFirstUsingFormattingRule.cs" />
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
using Microsoft.CodeAnalysis;
2+
using Microsoft.CodeAnalysis.CSharp;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Diagnostics;
7+
using System.Linq;
8+
using System.Text;
9+
using System.Threading;
10+
using System.Threading.Tasks;
11+
12+
namespace Microsoft.DotNet.CodeFormatting.Rules
13+
{
14+
internal sealed class ExplicitVisibilityRule : IFormattingRule
15+
{
16+
private sealed class VisibilityRewriter : CSharpSyntaxRewriter
17+
{
18+
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
19+
{
20+
node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node);
21+
var visibilityKind = GetTypeDefaultVisibility(node);
22+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
23+
}
24+
25+
public override SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
26+
{
27+
var visibilityKind = GetTypeDefaultVisibility(node);
28+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
29+
}
30+
31+
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
32+
{
33+
node = (StructDeclarationSyntax)base.VisitStructDeclaration(node);
34+
var visibilityKind = GetTypeDefaultVisibility(node);
35+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
36+
}
37+
38+
public override SyntaxNode VisitDelegateDeclaration(DelegateDeclarationSyntax node)
39+
{
40+
var visibilityKind = GetTypeDefaultVisibility(node);
41+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
42+
}
43+
44+
public override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
45+
{
46+
var visibilityKind = GetTypeDefaultVisibility(node);
47+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), visibilityKind);
48+
}
49+
50+
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
51+
{
52+
node = (MethodDeclarationSyntax)base.VisitMethodDeclaration(node);
53+
return EnsureVisibility(node, node.Modifiers, (x, l) => x.WithModifiers(l), SyntaxKind.PrivateKeyword);
54+
}
55+
56+
private static SyntaxKind GetTypeDefaultVisibility(SyntaxNode node)
57+
{
58+
return IsNestedDeclaration(node) ? SyntaxKind.PrivateKeyword : SyntaxKind.InternalKeyword;
59+
}
60+
61+
private static bool IsNestedDeclaration(SyntaxNode node)
62+
{
63+
var current = node.Parent;
64+
while (current != null)
65+
{
66+
if (SyntaxFacts.IsTypeDeclaration(current.CSharpKind()))
67+
{
68+
return true;
69+
}
70+
71+
current = current.Parent;
72+
}
73+
74+
return false;
75+
}
76+
77+
private static MemberDeclarationSyntax EnsureVisibility<T>(T node, SyntaxTokenList originalModifiers, Func<T, SyntaxTokenList, T> withModifiers, SyntaxKind visibilityKind) where T : MemberDeclarationSyntax
78+
{
79+
Debug.Assert(SyntaxFacts.IsAccessibilityModifier(visibilityKind));
80+
81+
if (originalModifiers.Any(x => SyntaxFacts.IsAccessibilityModifier(x.CSharpKind())))
82+
{
83+
return node;
84+
}
85+
86+
SyntaxTokenList modifierList;
87+
if (originalModifiers.Count == 0)
88+
{
89+
var leadingTrivia = node.GetLeadingTrivia();
90+
node = node.WithLeadingTrivia();
91+
92+
var visibilityToken = SyntaxFactory.Token(
93+
leadingTrivia,
94+
visibilityKind,
95+
SyntaxFactory.TriviaList(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")));
96+
97+
modifierList = SyntaxFactory.TokenList(visibilityToken);
98+
}
99+
else
100+
{
101+
var leadingTrivia = originalModifiers[0].LeadingTrivia;
102+
var visibilityToken = SyntaxFactory.Token(
103+
leadingTrivia,
104+
visibilityKind,
105+
SyntaxFactory.TriviaList(SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")));
106+
107+
var list = new List<SyntaxToken>();
108+
list.Add(visibilityToken);
109+
list.Add(originalModifiers[0].WithLeadingTrivia());
110+
for (int i = 1; i < originalModifiers.Count; i++)
111+
{
112+
list.Add(originalModifiers[i]);
113+
}
114+
115+
modifierList = SyntaxFactory.TokenList(list);
116+
}
117+
118+
return withModifiers(node, modifierList);
119+
}
120+
}
121+
122+
public async Task<Document> ProcessAsync(Document document, CancellationToken cancellationToken)
123+
{
124+
var syntaxNode = await document.GetSyntaxRootAsync(cancellationToken) as CSharpSyntaxNode;
125+
if (syntaxNode == null)
126+
{
127+
return document;
128+
}
129+
130+
var rewriter = new VisibilityRewriter();
131+
var newNode = rewriter.Visit(syntaxNode);
132+
return document.WithSyntaxRoot(newNode);
133+
}
134+
}
135+
}

0 commit comments

Comments
 (0)