提交 a7ea4ea9 编写于 作者: N Neal Gafter

add SyntaxFactory.ParseDeclaration

Fixes #367
上级 863af8be
......@@ -364,6 +364,15 @@ internal CompilationUnitSyntax ParseCompilationUnitCore()
}
private NamespaceDeclarationSyntax ParseNamespaceDeclaration()
{
_recursionDepth++;
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
var result = ParseNamespaceDeclarationCore();
_recursionDepth--;
return result;
}
private NamespaceDeclarationSyntax ParseNamespaceDeclarationCore()
{
if (this.IsIncrementalAndFactoryContextMatches && this.CurrentNodeKind == SyntaxKind.NamespaceDeclaration)
{
......@@ -2012,8 +2021,38 @@ private bool IsTypeDeclarationStart()
}
}
public MemberDeclarationSyntax ParseMemberDeclaration()
{
// Use a parent kind that causes inclusion of only member declarations that could appear in a struct
// e.g. including fixed member declarations, but not statements.
const SyntaxKind parentKind = SyntaxKind.StructDeclaration;
return ParseWithStackGuard(
() => this.ParseMemberDeclarationOrStatement(parentKind),
() => createEmptyNodeFunc());
// Creates a dummy declaration node to which we can attach a stack overflow message
MemberDeclarationSyntax createEmptyNodeFunc()
{
return _syntaxFactory.IncompleteMember(
new SyntaxList<AttributeListSyntax>(),
new SyntaxList<SyntaxToken>(),
CreateMissingIdentifierName()
);
}
}
// Returns null if we can't parse anything (even partially).
internal MemberDeclarationSyntax ParseMemberDeclarationOrStatement(SyntaxKind parentKind)
{
_recursionDepth++;
StackGuard.EnsureSufficientExecutionStack(_recursionDepth);
var result = ParseMemberDeclarationOrStatementCore(parentKind);
_recursionDepth--;
return result;
}
// Returns null if we can't parse anything (even partially).
private MemberDeclarationSyntax ParseMemberDeclarationOrStatement(SyntaxKind parentKind)
private MemberDeclarationSyntax ParseMemberDeclarationOrStatementCore(SyntaxKind parentKind)
{
// "top-level" expressions and statements should never occur inside an asynchronous context
Debug.Assert(!IsInAsync);
......
......@@ -85,3 +85,4 @@ override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.ClassifyCommonConversio
override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.ContainsSymbolsWithName(string name, Microsoft.CodeAnalysis.SymbolFilter filter = Microsoft.CodeAnalysis.SymbolFilter.TypeAndMember, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> bool
override Microsoft.CodeAnalysis.CSharp.CSharpCompilation.GetSymbolsWithName(string name, Microsoft.CodeAnalysis.SymbolFilter filter = Microsoft.CodeAnalysis.SymbolFilter.TypeAndMember, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> System.Collections.Generic.IEnumerable<Microsoft.CodeAnalysis.ISymbol>
static Microsoft.CodeAnalysis.CSharp.LanguageVersionFacts.TryParse(string version, out Microsoft.CodeAnalysis.CSharp.LanguageVersion result) -> bool
static Microsoft.CodeAnalysis.CSharp.SyntaxFactory.ParseMemberDeclaration(string text, int offset = 0, Microsoft.CodeAnalysis.ParseOptions options = null, bool consumeFullText = true) -> Microsoft.CodeAnalysis.CSharp.Syntax.MemberDeclarationSyntax
......@@ -1730,6 +1730,30 @@ public static StatementSyntax ParseStatement(string text, int offset = 0, ParseO
}
}
/// <summary>
/// Parse a MemberDeclarationSyntax. This includes all of the kinds of members that could occur in a type declaration.
/// If nothing resembling a valid member declaration is found in the input, returns null.
/// </summary>
/// <param name="text">The text of the declaration.</param>
/// <param name="offset">Optional offset into text.</param>
/// <param name="options">The optional parse options to use. If no options are specified default options are
/// used.</param>
/// <param name="consumeFullText">True if extra tokens in the input following a declaration should be treated as an error</param>
public static MemberDeclarationSyntax ParseMemberDeclaration(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true)
{
using (var lexer = MakeLexer(text, offset, (CSharpParseOptions)options))
using (var parser = MakeParser(lexer))
{
var node = parser.ParseMemberDeclaration();
if (node == null)
{
return null;
}
return (MemberDeclarationSyntax)(consumeFullText ? parser.ConsumeUnexpectedTokens(node) : node).CreateRed();
}
}
/// <summary>
/// Parse a CompilationUnitSyntax using the grammar rule for an entire compilation unit (file). To produce a
/// SyntaxTree instance, use CSharpSyntaxTree.ParseText instead.
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
using Xunit.Abstractions;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class MemberDeclarationParsingTests : ParsingTests
{
public MemberDeclarationParsingTests(ITestOutputHelper output) : base(output) { }
private MemberDeclarationSyntax ParseDeclaration(string text, int offset = 0, ParseOptions options = null)
{
return SyntaxFactory.ParseMemberDeclaration(text, offset, options);
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ParsePrivate()
{
UsingDeclaration("private",
// (1,8): error CS1519: Invalid token '' in class, struct, or interface member declaration
// private
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "").WithArguments("").WithLocation(1, 8)
);
N(SyntaxKind.IncompleteMember);
{
N(SyntaxKind.PrivateKeyword);
}
EOF();
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ParseEmpty()
{
Assert.Null(ParseDeclaration(""));
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ParseTrash()
{
Assert.Null(ParseDeclaration("+-!@#$%^&*()"));
}
[ConditionalFact(typeof(WindowsOnly))]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ParseOverflow()
{
const int n = 10000;
var sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
sb.Append("class A{\n");
}
for (int i = 0; i < n; i++)
{
sb.Append("}\n");
}
var d = SyntaxFactory.ParseMemberDeclaration(sb.ToString());
if (d.GetDiagnostics().Any()) // some platforms have extra deep stacks and can parse this
{
d.GetDiagnostics().Verify(
// error CS8078: An expression is too long or complex to compile
Diagnostic(ErrorCode.ERR_InsufficientStack, "")
);
}
}
[ConditionalFact(typeof(WindowsOnly))]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ParseOverflow2()
{
const int n = 10000;
var sb = new StringBuilder();
for (int i = 0; i < n; i++)
{
sb.Append("namespace ns {\n");
}
for (int i = 0; i < n; i++)
{
sb.Append("}\n");
}
// SyntaxFactory.ParseCompilationUnit has been hardened to be resilient to stack overflow at the same time.
var cu = SyntaxFactory.ParseCompilationUnit(sb.ToString());
if (cu.GetDiagnostics().Any()) // some platforms have extra deep stacks and can parse this
{
cu.GetDiagnostics().Verify(
// error CS8078: An expression is too long or complex to compile
Diagnostic(ErrorCode.ERR_InsufficientStack, "")
);
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void Statement()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("x = x + 1;", offset: 0, options: options, consumeFullText: true,
// (1,3): error CS1519: Invalid token '=' in class, struct, or interface member declaration
// x = x + 1;
Diagnostic(ErrorCode.ERR_InvalidMemberDecl, "=").WithArguments("=").WithLocation(1, 3),
// (1,1): error CS1073: Unexpected token '='
// x = x + 1;
Diagnostic(ErrorCode.ERR_UnexpectedToken, "x").WithArguments("=").WithLocation(1, 1)
);
N(SyntaxKind.IncompleteMember);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "x");
}
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void Namespace()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
var d = SyntaxFactory.ParseMemberDeclaration("namespace ns {}", options: options);
Assert.Null(d);
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void TypeDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("class C { }", options: options);
N(SyntaxKind.ClassDeclaration);
{
N(SyntaxKind.ClassKeyword);
N(SyntaxKind.IdentifierToken, "C");
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void MethodDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("void M() { }", options: options);
N(SyntaxKind.MethodDeclaration);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.VoidKeyword);
}
N(SyntaxKind.IdentifierToken, "M");
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void FieldDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("static int F1 = a, F2 = b;", options: options);
N(SyntaxKind.FieldDeclaration);
{
N(SyntaxKind.StaticKeyword);
N(SyntaxKind.VariableDeclaration);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "F1");
N(SyntaxKind.EqualsValueClause);
{
N(SyntaxKind.EqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "a");
}
}
}
N(SyntaxKind.CommaToken);
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "F2");
N(SyntaxKind.EqualsValueClause);
{
N(SyntaxKind.EqualsToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "b");
}
}
}
}
N(SyntaxKind.SemicolonToken);
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void CtorDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("public ThisClassName(int x) : base(x) { }", options: options);
N(SyntaxKind.ConstructorDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.IdentifierToken, "ThisClassName");
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.Parameter);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.IdentifierToken, "x");
}
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.BaseConstructorInitializer);
{
N(SyntaxKind.ColonToken);
N(SyntaxKind.BaseKeyword);
N(SyntaxKind.ArgumentList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.Argument);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "x");
}
}
N(SyntaxKind.CloseParenToken);
}
}
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void DtorDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("public ~ThisClassName() { }", options: options);
N(SyntaxKind.DestructorDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.TildeToken);
N(SyntaxKind.IdentifierToken, "ThisClassName");
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.Block);
{
N(SyntaxKind.OpenBraceToken);
N(SyntaxKind.CloseBraceToken);
}
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void ConversionDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("public implicit operator long(int x) => x;", options: options);
N(SyntaxKind.ConversionOperatorDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.ImplicitKeyword);
N(SyntaxKind.OperatorKeyword);
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.LongKeyword);
}
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.Parameter);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.IdentifierToken, "x");
}
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.ArrowExpressionClause);
{
N(SyntaxKind.EqualsGreaterThanToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "x");
}
}
N(SyntaxKind.SemicolonToken);
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void OperatorDeclaration()
{
foreach (var options in new[] { TestOptions.Script, TestOptions.Regular })
{
UsingDeclaration("public int operator +(int x, int y) => x + y;", options: options);
N(SyntaxKind.OperatorDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.OperatorKeyword);
N(SyntaxKind.PlusToken);
N(SyntaxKind.ParameterList);
{
N(SyntaxKind.OpenParenToken);
N(SyntaxKind.Parameter);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.IdentifierToken, "x");
}
N(SyntaxKind.CommaToken);
N(SyntaxKind.Parameter);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.IdentifierToken, "y");
}
N(SyntaxKind.CloseParenToken);
}
N(SyntaxKind.ArrowExpressionClause);
{
N(SyntaxKind.EqualsGreaterThanToken);
N(SyntaxKind.AddExpression);
{
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "x");
}
N(SyntaxKind.PlusToken);
N(SyntaxKind.IdentifierName);
{
N(SyntaxKind.IdentifierToken, "y");
}
}
}
N(SyntaxKind.SemicolonToken);
}
EOF();
}
}
[Fact]
[WorkItem(367, "https://github.com/dotnet/roslyn/issues/367")]
public void TrashAfterDeclaration()
{
UsingDeclaration("public int x; public int y", 0, null, true,
// (1,1): error CS1073: Unexpected token 'public'
// public int x; public int y
Diagnostic(ErrorCode.ERR_UnexpectedToken, "public int x;").WithArguments("public").WithLocation(1, 1)
);
N(SyntaxKind.FieldDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.VariableDeclaration);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "x");
}
}
N(SyntaxKind.SemicolonToken);
}
EOF();
UsingDeclaration("public int x; public int y", 0, null, false);
N(SyntaxKind.FieldDeclaration);
{
N(SyntaxKind.PublicKeyword);
N(SyntaxKind.VariableDeclaration);
{
N(SyntaxKind.PredefinedType);
{
N(SyntaxKind.IntKeyword);
}
N(SyntaxKind.VariableDeclarator);
{
N(SyntaxKind.IdentifierToken, "x");
}
}
N(SyntaxKind.SemicolonToken);
}
EOF();
}
}
}
......@@ -62,6 +62,30 @@ internal void UsingStatement(string text, params DiagnosticDescription[] expecte
UsingNode(node);
}
internal void UsingDeclaration(string text, params DiagnosticDescription[] expectedErrors)
{
var node = SyntaxFactory.ParseMemberDeclaration(text);
// we validate the text roundtrips
Assert.Equal(text, node.ToFullString());
var actualErrors = node.GetDiagnostics();
actualErrors.Verify(expectedErrors);
UsingNode(node);
}
internal void UsingDeclaration(string text, int offset = 0, ParseOptions options = null, bool consumeFullText = true, params DiagnosticDescription[] expectedErrors)
{
var node = SyntaxFactory.ParseMemberDeclaration(text, offset, options, consumeFullText);
if (consumeFullText)
{
// we validate the text roundtrips
Assert.Equal(text, node.ToFullString());
}
var actualErrors = node.GetDiagnostics();
actualErrors.Verify(expectedErrors);
UsingNode(node);
}
internal void UsingExpression(string text, ParseOptions options, params DiagnosticDescription[] expectedErrors)
{
var node = SyntaxFactory.ParseExpression(text, options: options);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册