734 lines
33 KiB
C#
734 lines
33 KiB
C#
|
// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
|
|||
|
|
|||
|
using System.Web.Razor.Generator;
|
|||
|
using System.Web.Razor.Parser;
|
|||
|
using System.Web.Razor.Parser.SyntaxTree;
|
|||
|
using System.Web.Razor.Resources;
|
|||
|
using System.Web.Razor.Test.Framework;
|
|||
|
using System.Web.Razor.Text;
|
|||
|
using System.Web.Razor.Tokenizer.Symbols;
|
|||
|
using Xunit;
|
|||
|
using Assert = Microsoft.TestCommon.AssertEx;
|
|||
|
|
|||
|
namespace System.Web.Razor.Test.Parser.CSharp
|
|||
|
{
|
|||
|
public class CSharpBlockTest : CsHtmlCodeParserTestBase
|
|||
|
{
|
|||
|
[Fact]
|
|||
|
public void ParseBlockMethodThrowsArgNullExceptionOnNullContext()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
CSharpCodeParser parser = new CSharpCodeParser();
|
|||
|
|
|||
|
// Act and Assert
|
|||
|
Assert.Throws<InvalidOperationException>(() => parser.ParseBlock(), RazorResources.Parser_Context_Not_Set);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideSingleLineComments()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"if(foo) {
|
|||
|
// bar } "" baz '
|
|||
|
zoop();
|
|||
|
}", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void NestedCodeBlockWithAtCausesError()
|
|||
|
{
|
|||
|
ParseBlockTest("if (true) { @if(false) { } }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if (true) { ").AsStatement(),
|
|||
|
new StatementBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("if(false) { }").AsStatement()
|
|||
|
),
|
|||
|
Factory.Code(" }").AsStatement()),
|
|||
|
new RazorError(
|
|||
|
String.Format(RazorResources.ParseError_Unexpected_Keyword_After_At,
|
|||
|
"if"),
|
|||
|
new SourceLocation(13, 0, 13)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void BalancingBracketsIgnoresStringLiteralCharactersAndBracketsInsideBlockComments()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(
|
|||
|
@"if(foo) {
|
|||
|
/* bar } "" */ ' baz } '
|
|||
|
zoop();
|
|||
|
}", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForKeyword()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("for(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsForeachKeyword()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("foreach(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsWhileKeyword()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("while(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsUsingKeywordFollowedByParen()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsUsingsNestedWithinOtherBlocks()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(foo) { using(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); } }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsIfKeywordWithNoElseBranches()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(int i = 0; i < 10; new Foo { Bar = \"baz\" }) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockAllowsEmptyBlockStatement()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(false) { }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTerminatesParenBalancingAtEOF()
|
|||
|
{
|
|||
|
ImplicitExpressionTest("Html.En(code()", "Html.En(code()",
|
|||
|
AcceptedCharacters.Any,
|
|||
|
new RazorError(
|
|||
|
String.Format(RazorResources.ParseError_Expected_CloseBracket_Before_EOF,
|
|||
|
"(", ")"),
|
|||
|
new SourceLocation(8, 0, 8)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenIfAndElseClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(foo) { bar(); } /* Foo */ /* Bar */ else { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenIfAndElseClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("if(foo) { bar(); } ", " else { baz(); }", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenElseIfAndElseClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(foo) { bar(); } else if(bar) { baz(); } /* Foo */ /* Bar */ else { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenElseIfAndElseClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("if(foo) { bar(); } else if(bar) { baz(); } ", " else { baz(); }", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenIfAndElseIfClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("if(foo) { bar(); } /* Foo */ /* Bar */ else if(bar) { baz(); }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenIfAndElseIfClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("if(foo) { bar(); } ", " else if(bar) { baz(); }");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenIfAndElseClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"if(foo) { bar(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
else { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenElseIfAndElseClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"if(foo) { bar(); } else if(bar) { baz(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
else { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenIfAndElseIfClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"if(foo) { bar(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
else if(bar) { baz(); }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesElseIfBranchesOfIfStatement()
|
|||
|
{
|
|||
|
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""bar } baz"");
|
|||
|
}";
|
|||
|
const string document = ifStatement + elseIfBranch;
|
|||
|
|
|||
|
SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesMultipleElseIfBranchesOfIfStatement()
|
|||
|
{
|
|||
|
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""bar } baz"");
|
|||
|
}";
|
|||
|
const string document = ifStatement + elseIfBranch + elseIfBranch + elseIfBranch + elseIfBranch;
|
|||
|
SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesMultipleElseIfBranchesOfIfStatementFollowedByOneElseBranch()
|
|||
|
{
|
|||
|
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""bar } baz"");
|
|||
|
}";
|
|||
|
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
|
|||
|
const string document = ifStatement + elseIfBranch + elseIfBranch + elseBranch;
|
|||
|
|
|||
|
SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockStopsParsingCodeAfterElseBranch()
|
|||
|
{
|
|||
|
const string ifStatement = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
const string elseIfBranch = @" else if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""bar } baz"");
|
|||
|
}";
|
|||
|
const string elseBranch = @" else { Debug.WriteLine(@""bar } baz""); }";
|
|||
|
const string document = ifStatement + elseIfBranch + elseBranch + elseIfBranch;
|
|||
|
const string expected = ifStatement + elseIfBranch + elseBranch;
|
|||
|
|
|||
|
ParseBlockTest(document, new StatementBlock(Factory.Code(expected).AsStatement().Accepts(AcceptedCharacters.None)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockStopsParsingIfIfStatementNotFollowedByElse()
|
|||
|
{
|
|||
|
const string document = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
|
|||
|
SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockAcceptsElseIfWithNoCondition()
|
|||
|
{
|
|||
|
// We don't want to be a full C# parser - If the else if is missing it's condition, the C# compiler can handle that, we have all the info we need to keep parsing
|
|||
|
const string ifBranch = @"if(int i = 0; i < 10; new Foo { Bar = ""baz"" }) {
|
|||
|
Debug.WriteLine(@""foo } bar"");
|
|||
|
}";
|
|||
|
const string elseIfBranch = @" else if { foo(); }";
|
|||
|
const string document = ifBranch + elseIfBranch;
|
|||
|
|
|||
|
SingleSpanBlockTest(document, BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesDoWhileBlock()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } while(foo != bar);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesDoWhileBlockMissingSemicolon()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } while(foo != bar)", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileCondition()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } while", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileConditionWithSemicolon()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } while;", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesDoWhileBlockMissingWhileClauseEntirely()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } narf;", "do { var foo = bar; }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenDoAndWhileClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("do { var foo = bar; } /* Foo */ /* Bar */ while(true);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenDoAndWhileClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"do { var foo = bar; }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
while(true);", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenDoAndWhileClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("do { var foo = bar; } ", " while(true);", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockCorrectlyParsesMarkupInDoWhileBlock()
|
|||
|
{
|
|||
|
ParseBlockTest("@do { var foo = bar; <p>Foo</p> foo++; } while (foo<bar>);",
|
|||
|
new StatementBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("do { var foo = bar;").AsStatement(),
|
|||
|
new MarkupBlock(
|
|||
|
Factory.Markup(" <p>Foo</p> ").Accepts(AcceptedCharacters.None)
|
|||
|
),
|
|||
|
Factory.Code("foo++; } while (foo<bar>);").AsStatement().Accepts(AcceptedCharacters.None)
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsSwitchKeyword()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"switch(foo) {
|
|||
|
case 0:
|
|||
|
break;
|
|||
|
case 1:
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
case 2:
|
|||
|
return;
|
|||
|
default:
|
|||
|
return;
|
|||
|
}", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSkipsParenthesisedExpressionAndThenBalancesBracesIfFirstIdentifierIsLockKeyword()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("lock(foo) { Debug.WriteLine(@\"foo } bar\"); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockHasErrorsIfNamespaceImportMissingSemicolon()
|
|||
|
{
|
|||
|
NamespaceImportTest("using Foo.Bar.Baz", " Foo.Bar.Baz", acceptedCharacters: AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace, location: new SourceLocation(17, 0, 17));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockHasErrorsIfNamespaceAliasMissingSemicolon()
|
|||
|
{
|
|||
|
NamespaceImportTest("using Foo.Bar.Baz = FooBarBaz", " Foo.Bar.Baz = FooBarBaz", acceptedCharacters: AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace, location: new SourceLocation(29, 0, 29));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesNamespaceImportWithSemicolonForUsingKeywordIfIsInValidFormat()
|
|||
|
{
|
|||
|
NamespaceImportTest("using Foo.Bar.Baz;", " Foo.Bar.Baz", AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockDoesntCaptureWhitespaceAfterUsing()
|
|||
|
{
|
|||
|
ParseBlockTest("using Foo ",
|
|||
|
new DirectiveBlock(
|
|||
|
Factory.Code("using Foo")
|
|||
|
.AsNamespaceImport(" Foo", CSharpCodeParser.UsingKeywordLength)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace)));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesNamespaceAliasWithSemicolonForUsingKeywordIfIsInValidFormat()
|
|||
|
{
|
|||
|
NamespaceImportTest("using FooBarBaz = FooBarBaz;", " FooBarBaz = FooBarBaz", AcceptedCharacters.NonWhiteSpace | AcceptedCharacters.WhiteSpace);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTerminatesUsingKeywordAtEOFAndOutputsFileCodeBlock()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("using ", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTerminatesSingleLineCommentAtEndOfFile()
|
|||
|
{
|
|||
|
const string document = "foreach(var f in Foo) { // foo bar baz";
|
|||
|
SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code,
|
|||
|
new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTerminatesBlockCommentAtEndOfFile()
|
|||
|
{
|
|||
|
const string document = "foreach(var f in Foo) { /* foo bar baz";
|
|||
|
SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code,
|
|||
|
new RazorError(String.Format(RazorResources.ParseError_BlockComment_Not_Terminated), 24, 0, 24),
|
|||
|
new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTerminatesSingleSlashAtEndOfFile()
|
|||
|
{
|
|||
|
const string document = "foreach(var f in Foo) { / foo bar baz";
|
|||
|
SingleSpanBlockTest(document, document, BlockType.Statement, SpanKind.Code,
|
|||
|
new RazorError(String.Format(RazorResources.ParseError_Expected_EndOfBlock_Before_EOF, "foreach", '}', '{'), SourceLocation.Zero));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenTryAndFinallyClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ finally { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenTryAndFinallyClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("try { bar(); } ", " finally { biz(); }", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenCatchAndFinallyClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { bar(); } catch(bar) { baz(); } /* Foo */ /* Bar */ finally { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenCatchAndFinallyClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("try { bar(); } catch(bar) { baz(); } ", " finally { biz(); }", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsBlockCommentBetweenTryAndCatchClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { bar(); } /* Foo */ /* Bar */ catch(bar) { baz(); }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsRazorCommentBetweenTryAndCatchClause()
|
|||
|
{
|
|||
|
RunRazorCommentBetweenClausesTest("try { bar(); }", " catch(bar) { baz(); }");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenTryAndFinallyClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"try { bar(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
finally { baz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenCatchAndFinallyClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"try { bar(); } catch(bar) { baz(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
finally { biz(); }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsLineCommentBetweenTryAndCatchClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest(@"try { bar(); }
|
|||
|
// Foo
|
|||
|
// Bar
|
|||
|
catch(bar) { baz(); }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsTryStatementWithNoAdditionalClauses()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { var foo = new { } }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsMarkupWithinTryClause()
|
|||
|
{
|
|||
|
RunSimpleWrappedMarkupTest("try {", " <p>Foo</p> ", "}");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsTryStatementWithOneCatchClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsMarkupWithinCatchClause()
|
|||
|
{
|
|||
|
RunSimpleWrappedMarkupTest("try { var foo = new { } } catch(Foo Bar Baz) {", " <p>Foo</p> ", "}");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsTryStatementWithMultipleCatchClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsExceptionLessCatchClauses()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { var foo = new { } } catch { var foo = new { } }", BlockType.Statement, SpanKind.Code);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsMarkupWithinAdditionalCatchClauses()
|
|||
|
{
|
|||
|
RunSimpleWrappedMarkupTest("try { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) { var foo = new { } } catch(Foo Bar Baz) {", " <p>Foo</p> ", "}");
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsTryStatementWithFinallyClause()
|
|||
|
{
|
|||
|
SingleSpanBlockTest("try { var foo = new { } } finally { var foo = new { } }", BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockSupportsMarkupWithinFinallyClause()
|
|||
|
{
|
|||
|
RunSimpleWrappedMarkupTest("try { var foo = new { } } finally {", " <p>Foo</p> ", "}", acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockStopsParsingCatchClausesAfterFinallyBlock()
|
|||
|
{
|
|||
|
string expectedContent = "try { var foo = new { } } finally { var foo = new { } }";
|
|||
|
SingleSpanBlockTest(expectedContent + " catch(Foo Bar Baz) { }", expectedContent, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockDoesNotAllowMultipleFinallyBlocks()
|
|||
|
{
|
|||
|
string expectedContent = "try { var foo = new { } } finally { var foo = new { } }";
|
|||
|
SingleSpanBlockTest(expectedContent + " finally { }", expectedContent, BlockType.Statement, SpanKind.Code, acceptedCharacters: AcceptedCharacters.None);
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockAcceptsTrailingDotIntoImplicitExpressionWhenEmbeddedInCode()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"if(foo) { @foo. }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { ").AsStatement(),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("foo.")
|
|||
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)
|
|||
|
),
|
|||
|
Factory.Code(" }").AsStatement()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByOpenParen()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"if(foo) { @(foo + bar) }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { ").AsStatement(),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.MetaCode("(").Accepts(AcceptedCharacters.None),
|
|||
|
Factory.Code("foo + bar").AsExpression(),
|
|||
|
Factory.MetaCode(")").Accepts(AcceptedCharacters.None)
|
|||
|
),
|
|||
|
Factory.Code(" }").AsStatement()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockParsesExpressionOnSwitchCharacterFollowedByIdentifierStart()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"if(foo) { @foo[4].bar() }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { ").AsStatement(),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("foo[4].bar()")
|
|||
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords, acceptTrailingDot: true)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)
|
|||
|
),
|
|||
|
Factory.Code(" }").AsStatement()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTreatsDoubleAtSignAsEscapeSequenceIfAtStatementStart()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"if(foo) { @@class.Foo() }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { ").AsStatement(),
|
|||
|
Factory.Code("@").Hidden(),
|
|||
|
Factory.Code("@class.Foo() }").AsStatement()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockTreatsAtSignsAfterFirstPairAsPartOfCSharpStatement()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"if(foo) { @@@@class.Foo() }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { ").AsStatement(),
|
|||
|
Factory.Code("@").Hidden(),
|
|||
|
Factory.Code("@@@class.Foo() }").AsStatement()
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParseBlockDoesNotParseMarkupStatementOrExpressionOnSwitchCharacterNotFollowedByOpenAngleOrColon()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest("if(foo) { @\"Foo\".ToString(); }",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("if(foo) { @\"Foo\".ToString(); }").AsStatement()));
|
|||
|
}
|
|||
|
|
|||
|
[Fact]
|
|||
|
public void ParsersCanNestRecursively()
|
|||
|
{
|
|||
|
// Arrange
|
|||
|
ParseBlockTest(@"foreach(var c in db.Categories) {
|
|||
|
<div>
|
|||
|
<h1>@c.Name</h1>
|
|||
|
<ul>
|
|||
|
@foreach(var p in c.Products) {
|
|||
|
<li><a href=""@Html.ActionUrl(""Products"", ""Detail"", new { id = p.Id })"">@p.Name</a></li>
|
|||
|
}
|
|||
|
</ul>
|
|||
|
</div>
|
|||
|
}",
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code("foreach(var c in db.Categories) {\r\n").AsStatement(),
|
|||
|
new MarkupBlock(
|
|||
|
Factory.Markup(" <div>\r\n <h1>"),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("c.Name")
|
|||
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|||
|
Factory.Markup("</h1>\r\n <ul>\r\n"),
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code(@" ").AsStatement(),
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("foreach(var p in c.Products) {\r\n").AsStatement(),
|
|||
|
new MarkupBlock(
|
|||
|
Factory.Markup(" <li><a"),
|
|||
|
new MarkupBlock(new AttributeBlockCodeGenerator("href", new LocationTagged<string>(" href=\"", 193, 5, 30), new LocationTagged<string>("\"", 256, 5, 93)),
|
|||
|
Factory.Markup(" href=\"").With(SpanCodeGenerator.Null),
|
|||
|
new MarkupBlock(new DynamicAttributeBlockCodeGenerator(new LocationTagged<string>(String.Empty, 200, 5, 37), 200, 5, 37),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("Html.ActionUrl(\"Products\", \"Detail\", new { id = p.Id })")
|
|||
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace))),
|
|||
|
Factory.Markup("\"").With(SpanCodeGenerator.Null)),
|
|||
|
Factory.Markup(">"),
|
|||
|
new ExpressionBlock(
|
|||
|
Factory.CodeTransition(),
|
|||
|
Factory.Code("p.Name")
|
|||
|
.AsImplicitExpression(CSharpCodeParser.DefaultKeywords)
|
|||
|
.Accepts(AcceptedCharacters.NonWhiteSpace)),
|
|||
|
Factory.Markup("</a></li>\r\n").Accepts(AcceptedCharacters.None)),
|
|||
|
Factory.Code(" }\r\n").AsStatement().Accepts(AcceptedCharacters.None)),
|
|||
|
Factory.Markup(" </ul>\r\n </div>\r\n")
|
|||
|
.Accepts(AcceptedCharacters.None)),
|
|||
|
Factory.Code(" }").AsStatement().Accepts(AcceptedCharacters.None)));
|
|||
|
}
|
|||
|
|
|||
|
private void RunRazorCommentBetweenClausesTest(string preComment, string postComment, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
|
|||
|
{
|
|||
|
ParseBlockTest(preComment + "@* Foo *@ @* Bar *@" + postComment,
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code(preComment).AsStatement(),
|
|||
|
new CommentBlock(
|
|||
|
Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition),
|
|||
|
Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None),
|
|||
|
Factory.Comment(" Foo ", CSharpSymbolType.RazorComment),
|
|||
|
Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None),
|
|||
|
Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition)
|
|||
|
),
|
|||
|
Factory.Code(" ").AsStatement(),
|
|||
|
new CommentBlock(
|
|||
|
Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition),
|
|||
|
Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None),
|
|||
|
Factory.Comment(" Bar ", CSharpSymbolType.RazorComment),
|
|||
|
Factory.MetaCode("*", CSharpSymbolType.RazorCommentStar).Accepts(AcceptedCharacters.None),
|
|||
|
Factory.CodeTransition(CSharpSymbolType.RazorCommentTransition)
|
|||
|
),
|
|||
|
Factory.Code(postComment).AsStatement().Accepts(acceptedCharacters)));
|
|||
|
}
|
|||
|
|
|||
|
private void RunSimpleWrappedMarkupTest(string prefix, string markup, string suffix, AcceptedCharacters acceptedCharacters = AcceptedCharacters.Any)
|
|||
|
{
|
|||
|
ParseBlockTest(prefix + markup + suffix,
|
|||
|
new StatementBlock(
|
|||
|
Factory.Code(prefix).AsStatement(),
|
|||
|
new MarkupBlock(
|
|||
|
Factory.Markup(markup).Accepts(AcceptedCharacters.None)
|
|||
|
),
|
|||
|
Factory.Code(suffix).AsStatement().Accepts(acceptedCharacters)
|
|||
|
));
|
|||
|
}
|
|||
|
|
|||
|
private void NamespaceImportTest(string content, string expectedNS, AcceptedCharacters acceptedCharacters = AcceptedCharacters.None, string errorMessage = null, SourceLocation? location = null)
|
|||
|
{
|
|||
|
var errors = new RazorError[0];
|
|||
|
if (!String.IsNullOrEmpty(errorMessage) && location.HasValue)
|
|||
|
{
|
|||
|
errors = new RazorError[]
|
|||
|
{
|
|||
|
new RazorError(errorMessage, location.Value)
|
|||
|
};
|
|||
|
}
|
|||
|
ParseBlockTest(content,
|
|||
|
new DirectiveBlock(
|
|||
|
Factory.Code(content)
|
|||
|
.AsNamespaceImport(expectedNS, CSharpCodeParser.UsingKeywordLength)
|
|||
|
.Accepts(acceptedCharacters)),
|
|||
|
errors);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|