Skip to content

Commit

Permalink
Add IDE tests for "file-scoped namespaces" (#54374)
Browse files Browse the repository at this point in the history
  • Loading branch information
RikkiGibson authored Jun 25, 2021
1 parent 930ec34 commit df58f94
Show file tree
Hide file tree
Showing 12 changed files with 177 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4439,5 +4439,17 @@ await TestAsync(
testHost,
RecordStruct("R"));
}

[Theory]
[CombinatorialData]
public async Task BasicFileScopedNamespaceClassification(TestHost testHost)
{
await TestAsync(
@"namespace NS;
class C { }",
testHost,
Namespace("NS"));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2151,5 +2151,23 @@ void Goo(object o)
Punctuation.CloseCurly,
Punctuation.CloseCurly);
}

[Theory]
[CombinatorialData]
public async Task BasicFileScopedNamespaceClassification(TestHost testHost)
{
await TestAsync(
@"namespace NS;
class C { }",
testHost,
Keyword("namespace"),
Namespace("NS"),
Punctuation.Semicolon,
Keyword("class"),
Class("C"),
Punctuation.OpenCurly,
Punctuation.CloseCurly);
}
}
}
18 changes: 18 additions & 0 deletions src/EditorFeatures/CSharpTest/Debugging/LocationInfoGetterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ void Method()
}", "Namespace.Class.Method()", 2);
}

[Fact, Trait(Traits.Feature, Traits.Features.DebuggingLocationName)]
[WorkItem(49000, "https://github.com/dotnet/roslyn/issues/49000")]
public async Task TestFileScopedNamespace()
{
// This test behavior is incorrect. This should be Namespace.Class.Method.
// See the associated WorkItem for details.
await TestAsync(
@"namespace Namespace;
class Class
{
void Method()
{
}$$
}
", "Class.Method()", 2);
}

[Fact, Trait(Traits.Feature, Traits.Features.DebuggingLocationName)]
[WorkItem(527668, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/527668")]
public async Task TestDottedNamespace()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ namespace NS
code,
indentationLine: 3,
expectedIndentation: 4);

AssertSmartIndent(
code,
indentationLine: 4,
expectedIndentation: 4);
}

[WpfFact]
Expand Down Expand Up @@ -203,6 +208,32 @@ namespace NS
expectedIndentation: 4);
}

[WpfFact]
[Trait(Traits.Feature, Traits.Features.SmartIndent)]
public void FileScopedNamespace()
{
var code = @"using System;
namespace NS;
";
AssertSmartIndent(
code,
indentationLine: 1,
expectedIndentation: 0);

AssertSmartIndent(
code,
indentationLine: 2,
expectedIndentation: 0);

AssertSmartIndent(
code,
indentationLine: 4,
expectedIndentation: 0);
}

[WpfFact]
[Trait(Traits.Feature, Traits.Features.SmartIndent)]
public void Class()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,5 +401,12 @@ public async Task TestAfterRecord()
await VerifyKeywordAsync(
@"record $$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterFileScopedNamespace()
{
await VerifyKeywordAsync(
@"namespace NS; $$");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,34 @@ await VerifyKeywordAsync(SourceCodeKind.Regular,
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterPreviousFileScopedNamespace()
{
await VerifyAbsenceAsync(SourceCodeKind.Regular,
@"namespace N;
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterUsingInFileScopedNamespace()
{
await VerifyAbsenceAsync(SourceCodeKind.Regular,
@"namespace N;
using U;
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterUsingInNamespace()
{
await VerifyKeywordAsync(SourceCodeKind.Regular,
@"namespace N
{
using U;
$$
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterPreviousNamespace_Interactive()
{
Expand All @@ -107,6 +135,26 @@ await VerifyAbsenceAsync(SourceCodeKind.Script,
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterExternInFileScopedNamespace()
{
await VerifyAbsenceAsync(SourceCodeKind.Regular,
@"namespace N;
extern alias A;
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterExternInNamespace()
{
await VerifyKeywordAsync(SourceCodeKind.Regular,
@"namespace N
{
extern alias A;
$$
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterUsing()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,14 @@ await VerifyKeywordAsync(
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestAfterFileScopedNamespace()
{
await VerifyKeywordAsync(
@"namespace N;
$$");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotAfterUsingKeyword_InsideNamespace()
{
Expand Down Expand Up @@ -416,6 +424,14 @@ namespace NS
{}");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestBeforeFileScopedNamespace()
{
await VerifyKeywordAsync(
@"$$
namespace NS;");
}

[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestBeforeClass()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context
// |
if (token.Kind() == SyntaxKind.SemicolonToken)
{
if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective))
if (token.Parent.IsKind(SyntaxKind.ExternAliasDirective, SyntaxKind.UsingDirective)
&& !token.Parent.Parent.IsKind(SyntaxKind.FileScopedNamespaceDeclaration))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ internal static bool IsUsingDirectiveContext(CSharpSyntaxContext context, bool f

// root: u|

// ns Goo { u|
// namespace N { u|

// namespace N; u|

// extern alias a;
// u|
Expand All @@ -98,8 +100,8 @@ internal static bool IsUsingDirectiveContext(CSharpSyntaxContext context, bool f
return IsValidContextAtTheRoot(context, originalToken, cancellationToken);
}

if (token.Kind() == SyntaxKind.OpenBraceToken &&
token.Parent.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax _))
if ((token.Kind() == SyntaxKind.OpenBraceToken && token.Parent.IsKind(SyntaxKind.NamespaceDeclaration))
|| (token.Kind() == SyntaxKind.SemicolonToken && token.Parent.IsKind(SyntaxKind.FileScopedNamespaceDeclaration)))
{
// a child using can't come before externs
var nextToken = originalToken.GetNextToken(includeSkipped: true);
Expand Down
16 changes: 16 additions & 0 deletions src/Workspaces/CSharpTest/Formatting/FormattingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10078,5 +10078,21 @@ await AssertFormatAsync(
object F = Func((A,B)((A,B)t)=>t);
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.Formatting)]
public async Task FileScopedNamespace()
{
await AssertFormatAsync(
expected: @"
namespace NS;
class C { }
",
code: @"
namespace NS;
class C { }
");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1046,7 +1046,7 @@ public static IEnumerable<MemberDeclarationSyntax> GetMembers(this SyntaxNode? n
{
case CompilationUnitSyntax compilation:
return compilation.Members;
case NamespaceDeclarationSyntax @namespace:
case BaseNamespaceDeclarationSyntax @namespace:
return @namespace.Members;
case TypeDeclarationSyntax type:
return type.Members;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -773,7 +773,7 @@ node is EnumMemberDeclarationSyntax ||

public bool IsTopLevelNodeWithMembers([NotNullWhen(true)] SyntaxNode? node)
{
return node is NamespaceDeclarationSyntax ||
return node is BaseNamespaceDeclarationSyntax ||
node is TypeDeclarationSyntax ||
node is EnumDeclarationSyntax;
}
Expand Down Expand Up @@ -857,7 +857,8 @@ public string GetDisplayName(SyntaxNode? node, DisplayNameOptions options, strin
case SyntaxKind.IncompleteMember:
return missingTokenPlaceholder;
case SyntaxKind.NamespaceDeclaration:
return GetName(((NamespaceDeclarationSyntax)node).Name, options);
case SyntaxKind.FileScopedNamespaceDeclaration:
return GetName(((BaseNamespaceDeclarationSyntax)node).Name, options);
case SyntaxKind.QualifiedName:
var qualified = (QualifiedNameSyntax)node;
return GetName(qualified.Left, options) + dotToken + GetName(qualified.Right, options);
Expand Down

0 comments on commit df58f94

Please sign in to comment.