Skip to content

Commit

Permalink
Merge pull request #3446 from sharwell/file-scoped-namespaces
Browse files Browse the repository at this point in the history
Expand support for file scoped namespaces
  • Loading branch information
sharwell committed Jan 28, 2022
2 parents f0cc162 + 32bfb49 commit e48ecc0
Show file tree
Hide file tree
Showing 40 changed files with 915 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace StyleCop.Analyzers.MaintainabilityRules
{
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
Expand All @@ -14,6 +15,7 @@ namespace StyleCop.Analyzers.MaintainabilityRules
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Lightup;

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

case SyntaxKindEx.FileScopedNamespaceDeclaration:
// Only one file-scoped namespace is allowed per syntax tree
throw new InvalidOperationException("This location is not reachable");

default:
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace StyleCop.Analyzers.NamingRules
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Lightup;

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

if (memberSyntax is NamespaceDeclarationSyntax)
if (BaseNamespaceDeclarationSyntaxWrapper.IsInstance(memberSyntax))
{
// namespaces are not symbols. So we are just renaming the namespace
Task<Document> RenameNamespace(CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ namespace StyleCop.Analyzers.OrderingRules
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using StyleCop.Analyzers.Helpers;
using StyleCop.Analyzers.Lightup;
using StyleCop.Analyzers.Settings.ObjectModel;

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

if (parentDeclaration is NamespaceDeclarationSyntax)
if (BaseNamespaceDeclarationSyntaxWrapper.IsInstance(parentDeclaration))
{
return HandleNamespaceDeclaration(memberToMove, (NamespaceDeclarationSyntax)parentDeclaration, elementOrder, syntaxRoot, indentationSettings);
return HandleBaseNamespaceDeclaration(memberToMove, (BaseNamespaceDeclarationSyntaxWrapper)parentDeclaration, elementOrder, syntaxRoot, indentationSettings);
}

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

private static SyntaxNode HandleNamespaceDeclaration(MemberOrderHelper memberOrder, NamespaceDeclarationSyntax namespaceDeclaration, ImmutableArray<OrderingTrait> elementOrder, SyntaxNode syntaxRoot, IndentationSettings indentationSettings)
private static SyntaxNode HandleBaseNamespaceDeclaration(MemberOrderHelper memberOrder, BaseNamespaceDeclarationSyntaxWrapper namespaceDeclaration, ImmutableArray<OrderingTrait> elementOrder, SyntaxNode syntaxRoot, IndentationSettings indentationSettings)
{
return OrderMember(memberOrder, namespaceDeclaration.Members, elementOrder, syntaxRoot, indentationSettings);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,12 @@ private static string DetermineIndentation(CompilationUnitSyntax compilationUnit
{
var rootNamespace = compilationUnit.Members.First(member => BaseNamespaceDeclarationSyntaxWrapper.IsInstance(member));
var indentationLevel = IndentationHelper.GetIndentationSteps(indentationSettings, rootNamespace);
usingsIndentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationLevel + 1);
if (!rootNamespace.IsKind(SyntaxKindEx.FileScopedNamespaceDeclaration))
{
indentationLevel++;
}

usingsIndentation = IndentationHelper.GenerateIndentationString(indentationSettings, indentationLevel);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,95 @@

namespace StyleCop.Analyzers.Test.CSharp10.NamingRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp9.NamingRules;
using Xunit;
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
StyleCop.Analyzers.NamingRules.SA1300ElementMustBeginWithUpperCaseLetter,
StyleCop.Analyzers.NamingRules.RenameToUpperCaseCodeFixProvider>;

public class SA1300CSharp10UnitTests : SA1300CSharp9UnitTests
{
[Fact]
public async Task TestUpperCaseFileScopedNamespaceAsync()
{
var testCode = @"namespace Test;";

await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestLowerCaseFileScopedNamespaceAsync()
{
var testCode = @"namespace {|#0:test|};";

var fixedCode = @"namespace Test;";

DiagnosticResult expected = Diagnostic().WithArguments("test").WithLocation(0);
await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestAllowedLowerCaseFileScopedNamespaceIsNotReportedAsync()
{
var customTestSettings = @"
{
""settings"": {
""namingRules"": {
""allowedNamespaceComponents"": [ ""eBay"" ]
}
}
}
";

var testCode = @"namespace eBay;";

await new CSharpTest
{
TestCode = testCode,
Settings = customTestSettings,
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestLowerCaseComlicatedFileScopedNamespaceAsync()
{
var testCode = @"namespace {|#0:test|}.{|#1:foo|}.{|#2:bar|};";

var fixedCode = @"namespace Test.Foo.Bar;";

DiagnosticResult[] expected = new[]
{
Diagnostic().WithArguments("test").WithLocation(0),
Diagnostic().WithArguments("foo").WithLocation(1),
Diagnostic().WithArguments("bar").WithLocation(2),
};

await VerifyCSharpFixAsync(testCode, expected, fixedCode, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestAllowedLowerCaseComplicatedFileScopedNamespaceIsNotReportedAsync()
{
var customTestSettings = @"
{
""settings"": {
""namingRules"": {
""allowedNamespaceComponents"": [ ""iPod"" ]
}
}
}
";

var testCode = @"namespace Apple.iPod.Library;";

await new CSharpTest
{
TestCode = testCode,
Settings = customTestSettings,
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,36 @@

namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.OrderingRules;
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
using Xunit;

public class SA1200CSharp10OutsideNamespaceUnitTests : SA1200CSharp9OutsideNamespaceUnitTests
{
[Fact]
public async Task TestInvalidUsingStatementsInFileScopedNamespaceAsync()
{
var testCode = @"namespace TestNamespace;
{|#0:using System;|}
{|#1:using System.Threading;|}
";
var fixedTestCode = @"using System;
using System.Threading;
namespace TestNamespace;
";

DiagnosticResult[] expectedResults =
{
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(0),
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorOutside).WithLocation(1),
};

await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,41 @@

namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
using Xunit;

public class SA1200CSharp10PreserveUnitTests : SA1200CSharp9PreserveUnitTests
{
[Fact]
public async Task TestValidUsingStatementsInFileScopedNamespaceAsync()
{
var testCode = @"namespace TestNamespace;
using System;
using System.Threading;
";

await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that having using statements in the compilation unit will not diagnostics, even if they could be
/// moved inside a file-scoped namespace.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestIgnoredUsingStatementsInCompilationUnitWithFileScopedNamespaceAsync()
{
var testCode = @"using System;
using System.Threading;
namespace TestNamespace;
";

await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,39 @@

namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.OrderingRules;
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
using Xunit;
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
StyleCop.Analyzers.OrderingRules.SA1200UsingDirectivesMustBePlacedCorrectly,
StyleCop.Analyzers.OrderingRules.UsingCodeFixProvider>;

public class SA1200CSharp10UnitTests : SA1200CSharp9UnitTests
{
[Fact]
public async Task TestInvalidUsingStatementsInCompilationUnitWithFileScopedNamespaceAsync()
{
var testCode = @"{|#0:using System;|}
{|#1:using System.Threading;|}
namespace TestNamespace;
";

var fixedTestCode = @"namespace TestNamespace;
using System;
using System.Threading;
";

DiagnosticResult[] expectedResults =
{
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorInside).WithLocation(0),
Diagnostic(SA1200UsingDirectivesMustBePlacedCorrectly.DescriptorInside).WithLocation(1),
};

await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,51 @@

namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
{
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using StyleCop.Analyzers.Test.CSharp9.OrderingRules;
using Xunit;
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
StyleCop.Analyzers.OrderingRules.SA1201ElementsMustAppearInTheCorrectOrder,
StyleCop.Analyzers.OrderingRules.ElementOrderCodeFixProvider>;

public class SA1201CSharp10UnitTests : SA1201CSharp9UnitTests
{
[Fact]
public async Task TestOuterOrderCorrectOrderWithFileScopedNamespaceAsync()
{
string testCode = @"namespace Foo;
public delegate void bar();
public enum TestEnum { }
public interface IFoo { }
public struct FooStruct { }
public class FooClass { }
";

await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

[Fact]
public async Task TestOuterOrderWrongOrderWithFileScopedNamespaceAsync()
{
string testCode = @"
namespace Foo;
public enum TestEnum { }
public delegate void {|#0:bar|}();
public interface IFoo { }
public class FooClass { }
public struct {|#1:FooStruct|} { }
";
var expected = new[]
{
Diagnostic().WithLocation(0).WithArguments("delegate", "enum"),
Diagnostic().WithLocation(1).WithArguments("struct", "class"),
};

await VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}
}
}

0 comments on commit e48ecc0

Please sign in to comment.