Skip to content

Commit e48ecc0

Browse files
authored
Merge pull request #3446 from sharwell/file-scoped-namespaces
Expand support for file scoped namespaces
2 parents f0cc162 + 32bfb49 commit e48ecc0

File tree

40 files changed

+915
-87
lines changed

40 files changed

+915
-87
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/MaintainabilityRules/SA1402CodeFixProvider.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
namespace StyleCop.Analyzers.MaintainabilityRules
55
{
6+
using System;
67
using System.Collections.Generic;
78
using System.Collections.Immutable;
89
using System.Composition;
@@ -14,6 +15,7 @@ namespace StyleCop.Analyzers.MaintainabilityRules
1415
using Microsoft.CodeAnalysis.CSharp;
1516
using Microsoft.CodeAnalysis.CSharp.Syntax;
1617
using StyleCop.Analyzers.Helpers;
18+
using StyleCop.Analyzers.Lightup;
1719

1820
/// <summary>
1921
/// Implements a code fix for <see cref="SA1402FileMayOnlyContainASingleType"/>.
@@ -89,6 +91,10 @@ private static async Task<Solution> GetTransformedSolutionAsync(Document documen
8991
nodesToRemoveFromExtracted.Add(child);
9092
break;
9193

94+
case SyntaxKindEx.FileScopedNamespaceDeclaration:
95+
// Only one file-scoped namespace is allowed per syntax tree
96+
throw new InvalidOperationException("This location is not reachable");
97+
9298
default:
9399
break;
94100
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/NamingRules/RenameToUpperCaseCodeFixProvider.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace StyleCop.Analyzers.NamingRules
1414
using Microsoft.CodeAnalysis.CSharp;
1515
using Microsoft.CodeAnalysis.CSharp.Syntax;
1616
using StyleCop.Analyzers.Helpers;
17+
using StyleCop.Analyzers.Lightup;
1718

1819
/// <summary>
1920
/// Implements a code fix for all analyzers that require a symbol to be upper case.
@@ -60,7 +61,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6061
var newName = baseName;
6162
var memberSyntax = RenameHelper.GetParentDeclaration(token);
6263

63-
if (memberSyntax is NamespaceDeclarationSyntax)
64+
if (BaseNamespaceDeclarationSyntaxWrapper.IsInstance(memberSyntax))
6465
{
6566
// namespaces are not symbols. So we are just renaming the namespace
6667
Task<Document> RenameNamespace(CancellationToken cancellationToken)

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/ElementOrderCodeFixProvider.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ namespace StyleCop.Analyzers.OrderingRules
1515
using Microsoft.CodeAnalysis.CSharp;
1616
using Microsoft.CodeAnalysis.CSharp.Syntax;
1717
using StyleCop.Analyzers.Helpers;
18+
using StyleCop.Analyzers.Lightup;
1819
using StyleCop.Analyzers.Settings.ObjectModel;
1920

2021
/// <summary>
@@ -82,9 +83,9 @@ private static SyntaxNode UpdateSyntaxRoot(MemberDeclarationSyntax memberDeclara
8283
return HandleTypeDeclaration(memberToMove, (TypeDeclarationSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationSettings);
8384
}
8485

85-
if (parentDeclaration is NamespaceDeclarationSyntax)
86+
if (BaseNamespaceDeclarationSyntaxWrapper.IsInstance(parentDeclaration))
8687
{
87-
return HandleNamespaceDeclaration(memberToMove, (NamespaceDeclarationSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationSettings);
88+
return HandleBaseNamespaceDeclaration(memberToMove, (BaseNamespaceDeclarationSyntaxWrapper)parentDeclaration, elementOrder, syntaxRoot, indentationSettings);
8889
}
8990

9091
if (parentDeclaration is CompilationUnitSyntax)
@@ -105,7 +106,7 @@ private static SyntaxNode HandleCompilationUnitDeclaration(MemberOrderHelper mem
105106
return OrderMember(memberOrder, compilationUnitDeclaration.Members, elementOrder, syntaxRoot, indentationSettings);
106107
}
107108

108-
private static SyntaxNode HandleNamespaceDeclaration(MemberOrderHelper memberOrder, NamespaceDeclarationSyntax namespaceDeclaration, ImmutableArray<OrderingTrait> elementOrder, SyntaxNode syntaxRoot, IndentationSettings indentationSettings)
109+
private static SyntaxNode HandleBaseNamespaceDeclaration(MemberOrderHelper memberOrder, BaseNamespaceDeclarationSyntaxWrapper namespaceDeclaration, ImmutableArray<OrderingTrait> elementOrder, SyntaxNode syntaxRoot, IndentationSettings indentationSettings)
109110
{
110111
return OrderMember(memberOrder, namespaceDeclaration.Members, elementOrder, syntaxRoot, indentationSettings);
111112
}

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,12 @@ private static string DetermineIndentation(CompilationUnitSyntax compilationUnit
137137
{
138138
var rootNamespace = compilationUnit.Members.First(member => BaseNamespaceDeclarationSyntaxWrapper.IsInstance(member));
139139
var indentationLevel = IndentationHelper.GetIndentationSteps(indentationSettings, rootNamespace);
140-
usingsIndentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationLevel + 1);
140+
if (!rootNamespace.IsKind(SyntaxKindEx.FileScopedNamespaceDeclaration))
141+
{
142+
indentationLevel++;
143+
}
144+
145+
usingsIndentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationLevel);
141146
}
142147
else
143148
{

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/NamingRules/SA1300CSharp10UnitTests.cs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,95 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.NamingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp9.NamingRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.NamingRules.SA1300ElementMustBeginWithUpperCaseLetter,
13+
StyleCop.Analyzers.NamingRules.RenameToUpperCaseCodeFixProvider>;
714

815
public class SA1300CSharp10UnitTests : SA1300CSharp9UnitTests
916
{
17+
[Fact]
18+
public async Task TestUpperCaseFileScopedNamespaceAsync()
19+
{
20+
var testCode = @"namespace Test;";
21+
22+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
23+
}
24+
25+
[Fact]
26+
public async Task TestLowerCaseFileScopedNamespaceAsync()
27+
{
28+
var testCode = @"namespace {|#0:test|};";
29+
30+
var fixedCode = @"namespace Test;";
31+
32+
DiagnosticResult expected = Diagnostic().WithArguments("test").WithLocation(0);
33+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
34+
}
35+
36+
[Fact]
37+
public async Task TestAllowedLowerCaseFileScopedNamespaceIsNotReportedAsync()
38+
{
39+
var customTestSettings = @"
40+
{
41+
""settings"": {
42+
""namingRules"": {
43+
""allowedNamespaceComponents"": [ ""eBay"" ]
44+
}
45+
}
46+
}
47+
";
48+
49+
var testCode = @"namespace eBay;";
50+
51+
await new CSharpTest
52+
{
53+
TestCode = testCode,
54+
Settings = customTestSettings,
55+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
56+
}
57+
58+
[Fact]
59+
public async Task TestLowerCaseComlicatedFileScopedNamespaceAsync()
60+
{
61+
var testCode = @"namespace {|#0:test|}.{|#1:foo|}.{|#2:bar|};";
62+
63+
var fixedCode = @"namespace Test.Foo.Bar;";
64+
65+
DiagnosticResult[] expected = new[]
66+
{
67+
Diagnostic().WithArguments("test").WithLocation(0),
68+
Diagnostic().WithArguments("foo").WithLocation(1),
69+
Diagnostic().WithArguments("bar").WithLocation(2),
70+
};
71+
72+
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
73+
}
74+
75+
[Fact]
76+
public async Task TestAllowedLowerCaseComplicatedFileScopedNamespaceIsNotReportedAsync()
77+
{
78+
var customTestSettings = @"
79+
{
80+
""settings"": {
81+
""namingRules"": {
82+
""allowedNamespaceComponents"": [ ""iPod"" ]
83+
}
84+
}
85+
}
86+
";
87+
88+
var testCode = @"namespace Apple.iPod.Library;";
89+
90+
await new CSharpTest
91+
{
92+
TestCode = testCode,
93+
Settings = customTestSettings,
94+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
95+
}
1096
}
1197
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10OutsideNamespaceUnitTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,36 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
9+
using StyleCop.Analyzers.OrderingRules;
610
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
11+
using Xunit;
712

813
public class SA1200CSharp10OutsideNamespaceUnitTests : SA1200CSharp9OutsideNamespaceUnitTests
914
{
15+
[Fact]
16+
public async Task TestInvalidUsingStatementsInFileScopedNamespaceAsync()
17+
{
18+
var testCode = @"namespace TestNamespace;
19+
20+
{|#0:using System;|}
21+
{|#1:using System.Threading;|}
22+
";
23+
var fixedTestCode = @"using System;
24+
using System.Threading;
25+
26+
namespace TestNamespace;
27+
";
28+
29+
DiagnosticResult[] expectedResults =
30+
{
31+
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(0),
32+
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(1),
33+
};
34+
35+
await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
36+
}
1037
}
1138
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10PreserveUnitTests.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,41 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
10+
using Xunit;
711

812
public class SA1200CSharp10PreserveUnitTests : SA1200CSharp9PreserveUnitTests
913
{
14+
[Fact]
15+
public async Task TestValidUsingStatementsInFileScopedNamespaceAsync()
16+
{
17+
var testCode = @"namespace TestNamespace;
18+
19+
using System;
20+
using System.Threading;
21+
";
22+
23+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
24+
}
25+
26+
/// <summary>
27+
/// Verifies that having using statements in the compilation unit will not diagnostics, even if they could be
28+
/// moved inside a file-scoped namespace.
29+
/// </summary>
30+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
31+
[Fact]
32+
public async Task TestIgnoredUsingStatementsInCompilationUnitWithFileScopedNamespaceAsync()
33+
{
34+
var testCode = @"using System;
35+
using System.Threading;
36+
37+
namespace TestNamespace;
38+
";
39+
40+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
41+
}
1042
}
1143
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,39 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
9+
using StyleCop.Analyzers.OrderingRules;
610
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
11+
using Xunit;
12+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
13+
StyleCop.Analyzers.OrderingRules.SA1200UsingDirectivesMustBePlacedCorrectly,
14+
StyleCop.Analyzers.OrderingRules.UsingCodeFixProvider>;
715

816
public class SA1200CSharp10UnitTests : SA1200CSharp9UnitTests
917
{
18+
[Fact]
19+
public async Task TestInvalidUsingStatementsInCompilationUnitWithFileScopedNamespaceAsync()
20+
{
21+
var testCode = @"{|#0:using System;|}
22+
{|#1:using System.Threading;|}
23+
24+
namespace TestNamespace;
25+
";
26+
27+
var fixedTestCode = @"namespace TestNamespace;
28+
using System;
29+
using System.Threading;
30+
";
31+
32+
DiagnosticResult[] expectedResults =
33+
{
34+
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorInside).WithLocation(0),
35+
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorInside).WithLocation(1),
36+
};
37+
38+
await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
39+
}
1040
}
1141
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1201CSharp10UnitTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,51 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis.Testing;
69
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
10+
using Xunit;
11+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
12+
StyleCop.Analyzers.OrderingRules.SA1201ElementsMustAppearInTheCorrectOrder,
13+
StyleCop.Analyzers.OrderingRules.ElementOrderCodeFixProvider>;
714

815
public class SA1201CSharp10UnitTests : SA1201CSharp9UnitTests
916
{
17+
[Fact]
18+
public async Task TestOuterOrderCorrectOrderWithFileScopedNamespaceAsync()
19+
{
20+
string testCode = @"namespace Foo;
21+
22+
public delegate void bar();
23+
public enum TestEnum { }
24+
public interface IFoo { }
25+
public struct FooStruct { }
26+
public class FooClass { }
27+
";
28+
29+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
30+
}
31+
32+
[Fact]
33+
public async Task TestOuterOrderWrongOrderWithFileScopedNamespaceAsync()
34+
{
35+
string testCode = @"
36+
namespace Foo;
37+
38+
public enum TestEnum { }
39+
public delegate void {|#0:bar|}();
40+
public interface IFoo { }
41+
public class FooClass { }
42+
public struct {|#1:FooStruct|} { }
43+
";
44+
var expected = new[]
45+
{
46+
Diagnostic().WithLocation(0).WithArguments("delegate", "enum"),
47+
Diagnostic().WithLocation(1).WithArguments("struct", "class"),
48+
};
49+
50+
await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
51+
}
1052
}
1153
}

0 commit comments

Comments
 (0)