diff --git a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Statements.cs b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Statements.cs index f154b380a0..29269ce41b 100644 --- a/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Statements.cs +++ b/src/Microsoft.AspNet.Razor/Parser/CSharpCodeParser.Statements.cs @@ -101,7 +101,7 @@ namespace Microsoft.AspNet.Razor.Parser // using ( ==> Using Statement UsingStatement(block); } - else if (At(CSharpSymbolType.Identifier)) + else if (At(CSharpSymbolType.Identifier) || At(CSharpKeyword.Static)) { // using Identifier ==> Using Declaration if (!topLevel) @@ -126,31 +126,40 @@ namespace Microsoft.AspNet.Razor.Parser // Set block type to directive Context.CurrentBlock.Type = BlockType.Directive; - // Parse a type name - Assert(CSharpSymbolType.Identifier); - NamespaceOrTypeName(); - IEnumerable ws = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); - if (At(CSharpSymbolType.Assign)) + if (At(CSharpSymbolType.Identifier)) { - // Alias - Accept(ws); - Assert(CSharpSymbolType.Assign); - AcceptAndMoveNext(); - - AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); - - // One more namespace or type name + // non-static using NamespaceOrTypeName(); + var whitespace = ReadWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); + if (At(CSharpSymbolType.Assign)) + { + // Alias + Accept(whitespace); + Assert(CSharpSymbolType.Assign); + AcceptAndMoveNext(); + + AcceptWhile(IsSpacingToken(includeNewLines: true, includeComments: true)); + + // One more namespace or type name + NamespaceOrTypeName(); + } + else + { + PutCurrentBack(); + PutBack(whitespace); + } } - else + else if (At(CSharpKeyword.Static)) { - PutCurrentBack(); - PutBack(ws); + // static using + AcceptAndMoveNext(); + AcceptWhile(IsSpacingToken(includeNewLines: false, includeComments: true)); + NamespaceOrTypeName(); } Span.EditHandler.AcceptedCharacters = AcceptedCharacters.AnyExceptNewline; Span.CodeGenerator = new AddImportCodeGenerator( - Span.GetContent(syms => syms.Skip(1))); + Span.GetContent(symbols => symbols.Skip(1))); // Optional ";" if (EnsureCurrent()) diff --git a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs index 4c0330d1f8..417c48c2c9 100644 --- a/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Generator/CSharpRazorCodeGeneratorTest.cs @@ -176,8 +176,11 @@ namespace Microsoft.AspNet.Razor.Test.Generator BuildLineMapping(1, 0, 1, 51, 3, 0, 15), BuildLineMapping(19, 1, 1, 132, 9, 0, 32), BuildLineMapping(54, 2, 1, 230, 15, 0, 12), - BuildLineMapping(99, 4, 772, 39, 29, 21), - BuildLineMapping(161, 5, 888, 44, 35, 20) + BuildLineMapping(71, 4, 1, 308, 21, 0, 19), + BuildLineMapping(93, 5, 1, 393, 27, 0, 27), + BuildLineMapping(123, 6, 1, 486, 33, 0, 41), + BuildLineMapping(197, 8, 1057, 57, 29, 21), + BuildLineMapping(259, 9, 1174, 62, 35, 20) }); } diff --git a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs index a28c31dfe6..e46b76850d 100644 --- a/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs +++ b/test/Microsoft.AspNet.Razor.Test/Parser/CSharp/CSharpStatementTest.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using Microsoft.AspNet.Razor.Generator; using Microsoft.AspNet.Razor.Parser; using Microsoft.AspNet.Razor.Parser.SyntaxTree; @@ -148,6 +149,53 @@ namespace Microsoft.AspNet.Razor.Test.Parser.CSharp )); } + public static TheoryData StaticUsingData + { + get + { + var factory = SpanFactory.CreateCsHtml(); + Func createUsing = (code, import) => + new DirectiveBlock( + factory.CodeTransition(), + factory.Code(code) + .AsNamespaceImport(import) + .Accepts(AcceptedCharacters.AnyExceptNewline)); + + // document, expectedResult + return new TheoryData + { + { "@using static", createUsing("using static", " static") }, + { "@using static ", createUsing("using static ", " static ") }, + { "@using static ", createUsing("using static ", " static ") }, + { "@using static System", createUsing("using static System", " static System") }, + { + "@using static System", + createUsing("using static System", " static System") + }, + { + "@using static System.Console", + createUsing("using static System.Console", " static System.Console") + }, + { + "@using static global::System.Console", + createUsing("using static global::System.Console", " static global::System.Console") + }, + { + "@using static global::System.Console ", + createUsing("using static global::System.Console", " static global::System.Console") + }, + }; + } + } + + [Theory] + [MemberData(nameof(StaticUsingData))] + public void StaticUsingImport(string document, DirectiveBlock expectedResult) + { + // Act & Assert + ParseBlockTest(document, expectedResult); + } + [Fact] public void UsingStatement() { diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs index 1f7d95f9a1..0c952d7cd0 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.DesignTime.cs @@ -15,6 +15,24 @@ using Foo = System.Text.Encoding #line 3 "Imports.cshtml" using System +#line default +#line hidden + ; +#line 5 "Imports.cshtml" +using static System + +#line default +#line hidden + ; +#line 6 "Imports.cshtml" +using static System.Console + +#line default +#line hidden + ; +#line 7 "Imports.cshtml" +using static global::System.Text.Encoding + #line default #line hidden ; @@ -36,12 +54,12 @@ using System #pragma warning disable 1998 public override async Task ExecuteAsync() { -#line 5 "Imports.cshtml" +#line 9 "Imports.cshtml" __o = typeof(Path).FullName; #line default #line hidden -#line 6 "Imports.cshtml" +#line 10 "Imports.cshtml" __o = typeof(Foo).FullName; #line default diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs index 689763e5a9..a44bf96adf 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Output/Imports.cs @@ -1,4 +1,4 @@ -#pragma checksum "Imports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "1b57def0ce2632d0f50d63dbdc5e0e719c202863" +#pragma checksum "Imports.cshtml" "{ff1816ec-aa5e-4d10-87f7-6f4963833460}" "f452adb7c255f6d9d6d2573c6add7cb28022b151" namespace TestOutput { #line 1 "Imports.cshtml" @@ -16,6 +16,24 @@ using Foo = System.Text.Encoding #line 3 "Imports.cshtml" using System +#line default +#line hidden + ; +#line 5 "Imports.cshtml" +using static System + +#line default +#line hidden + ; +#line 6 "Imports.cshtml" +using static System.Console + +#line default +#line hidden + ; +#line 7 "Imports.cshtml" +using static global::System.Text.Encoding + #line default #line hidden ; @@ -31,27 +49,30 @@ using System #pragma warning disable 1998 public override async Task ExecuteAsync() { - Instrumentation.BeginContext(68, 30, true); + Instrumentation.BeginContext(68, 2, true); + WriteLiteral("\r\n"); + Instrumentation.EndContext(); + Instrumentation.BeginContext(166, 30, true); WriteLiteral("\r\n

Path\'s full type name is "); Instrumentation.EndContext(); - Instrumentation.BeginContext(99, 21, false); -#line 5 "Imports.cshtml" + Instrumentation.BeginContext(197, 21, false); +#line 9 "Imports.cshtml" Write(typeof(Path).FullName); #line default #line hidden Instrumentation.EndContext(); - Instrumentation.BeginContext(120, 40, true); + Instrumentation.BeginContext(218, 40, true); WriteLiteral("

\r\n

Foo\'s actual full type name is "); Instrumentation.EndContext(); - Instrumentation.BeginContext(161, 20, false); -#line 6 "Imports.cshtml" + Instrumentation.BeginContext(259, 20, false); +#line 10 "Imports.cshtml" Write(typeof(Foo).FullName); #line default #line hidden Instrumentation.EndContext(); - Instrumentation.BeginContext(181, 4, true); + Instrumentation.BeginContext(279, 4, true); WriteLiteral("

"); Instrumentation.EndContext(); } diff --git a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml index 332e26f022..90b2ae8222 100644 --- a/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml +++ b/test/Microsoft.AspNet.Razor.Test/TestFiles/CodeGenerator/CS/Source/Imports.cshtml @@ -2,5 +2,9 @@ @using Foo = System.Text.Encoding @using System +@using static System +@using static System.Console +@using static global::System.Text.Encoding +

Path's full type name is @typeof(Path).FullName

Foo's actual full type name is @typeof(Foo).FullName

\ No newline at end of file