-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add helper methods to Verify operation tree for annonated text within… #17856
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,9 +6,8 @@ | |
using Microsoft.CodeAnalysis.CSharp.Symbols; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
using Microsoft.CodeAnalysis.CSharp.Test.Utilities; | ||
using Microsoft.CodeAnalysis.Text; | ||
using Microsoft.CodeAnalysis.Test.Utilities; | ||
using Xunit; | ||
using Roslyn.Test.Utilities; | ||
|
||
namespace Microsoft.CodeAnalysis.CSharp.UnitTests | ||
{ | ||
|
@@ -220,5 +219,41 @@ protected CompilationUtils.SemanticInfoSummary GetSemanticInfoForTest(string tes | |
{ | ||
return GetSemanticInfoForTest<ExpressionSyntax>(testSrc); | ||
} | ||
|
||
protected string GetOperationTreeForTest<TSyntaxNode>(CSharpCompilation compilation) | ||
where TSyntaxNode: SyntaxNode | ||
{ | ||
var tree = compilation.SyntaxTrees[0]; | ||
var model = compilation.GetSemanticModel(tree); | ||
SyntaxNode syntaxNode = GetSyntaxNodeOfTypeForBinding<TSyntaxNode>(GetSyntaxNodeList(tree)); | ||
if (syntaxNode == null) | ||
{ | ||
return null; | ||
} | ||
|
||
var operation = model.GetOperationInternal(syntaxNode); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can a node have multiple operations? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, and we need to test that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so when testing.. do we want to explicit on IOperation we get from the API? or we use the default behavior? can user get IOperation off a node? or is it only internal, testing stuff? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a API discussion I believe. SemanticModel.GetOperation doesn't seem to take an options, but we have an internal enum GetOperationOptions that decides what bound node to choose for clashes. Until we expose that publically, unit tests should just use the default that the GetOperation API passes down. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Filed #17861 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. so for users, they expect 1 node to have 1 IOperation 1:1 matching? hmm.. but when they examine SyntaxNode of IOperation, they might get multiple IOperation with same node right? how users get to those IOperations? only through walker? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, seems we need to expose the options when there are conflict - needs design discussion. You can probably add comment to #17861 |
||
return operation != null ? OperationTreeVerifier.GetOperationTree(operation) : null; | ||
} | ||
|
||
protected string GetOperationTreeForTest<TSyntaxNode>(string testSrc, string expectedOperationTree, CSharpParseOptions parseOptions = null) | ||
where TSyntaxNode : SyntaxNode | ||
{ | ||
var compilation = CreateCompilationWithMscorlib(testSrc, new[] { SystemCoreRef }, parseOptions: parseOptions); | ||
return GetOperationTreeForTest<TSyntaxNode>(compilation); | ||
} | ||
|
||
protected void VerifyOperationTreeForTest<TSyntaxNode>(CSharpCompilation compilation, string expectedOperationTree) | ||
where TSyntaxNode : SyntaxNode | ||
{ | ||
var actualOperationTree = GetOperationTreeForTest<TSyntaxNode>(compilation); | ||
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); | ||
} | ||
|
||
protected void VerifyOperationTreeForTest<TSyntaxNode>(string testSrc, string expectedOperationTree, CSharpParseOptions parseOptions = null) | ||
where TSyntaxNode : SyntaxNode | ||
{ | ||
var actualOperationTree = GetOperationTreeForTest<TSyntaxNode>(testSrc, expectedOperationTree, parseOptions); | ||
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
Imports Microsoft.CodeAnalysis.Test.Utilities | ||
Imports Microsoft.CodeAnalysis.Text | ||
Imports Microsoft.CodeAnalysis.VisualBasic.Symbols | ||
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax | ||
|
@@ -151,4 +152,32 @@ Public MustInherit Class SemanticModelTestBase : Inherits BasicTestBase | |
).Where(Function(s) anyArity OrElse DirectCast(s, Symbol).GetArity() = arity.Value).ToList() | ||
End Function | ||
|
||
Friend Function GetOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(compilation As VisualBasicCompilation, fileName As String, Optional which As Integer = 0) As String | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. interesting so compiler test framework between VB and C# is different slightly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it seems there are differences between binding test framework helpers, and I had to retain them so I can re-use it. |
||
Dim node As SyntaxNode = CompilationUtils.FindBindingText(Of TSyntaxNode)(compilation, fileName, which) | ||
If node Is Nothing Then | ||
Return Nothing | ||
End If | ||
|
||
Dim tree = (From t In compilation.SyntaxTrees Where t.FilePath = fileName).Single() | ||
Dim semanticModel = compilation.GetSemanticModel(tree) | ||
Dim operation = semanticModel.GetOperationInternal(node) | ||
Return If(operation IsNot Nothing, OperationTreeVerifier.GetOperationTree(operation), Nothing) | ||
End Function | ||
|
||
Friend Function GetOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) As String | ||
Dim fileName = "a.vb" | ||
Dim syntaxTree = Parse(testSrc, fileName, parseOptions) | ||
Dim compilation = CreateCompilationWithMscorlib(syntaxTree) | ||
Return GetOperationTreeForTest(Of TSyntaxNode)(compilation, fileName, which) | ||
End Function | ||
|
||
Friend Sub VerifyOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(compilation As VisualBasicCompilation, fileName As String, expectedOperationTree As String, Optional which As Integer = 0) | ||
Dim actualOperationTree = GetOperationTreeForTest(Of TSyntaxNode)(compilation, fileName, which) | ||
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree) | ||
End Sub | ||
|
||
Friend Sub VerifyOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, expectedOperationTree As String, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) | ||
Dim actualOperationTree = GetOperationTreeForTest(Of TSyntaxNode)(testSrc, parseOptions, which) | ||
OperationTreeVerifier.Verify(expectedOperationTree, actualOperationTree) | ||
End Sub | ||
End Class |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,10 @@ | |
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.Text; | ||
using System.Text.RegularExpressions; | ||
using Microsoft.CodeAnalysis.Semantics; | ||
using Microsoft.CodeAnalysis.Test.Extensions; | ||
using Roslyn.Test.Utilities; | ||
using Xunit; | ||
|
||
namespace Microsoft.CodeAnalysis.Test.Utilities | ||
|
@@ -42,6 +44,15 @@ public static string GetOperationTree(IOperation operation, int initialIndent = | |
return walker._builder.ToString(); | ||
} | ||
|
||
public static void Verify(string expectedOperationTree, string actualOperationTree) | ||
{ | ||
char[] newLineChars = Environment.NewLine.ToCharArray(); | ||
string actual = actualOperationTree.Trim(newLineChars); | ||
expectedOperationTree = expectedOperationTree.Trim(newLineChars); | ||
expectedOperationTree = Regex.Replace(expectedOperationTree, "([^\r])\n", "$1" + Environment.NewLine); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what does it actually trying to do? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is due to the CDATA for VB. It seems to have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. hmm.. but you trimmed new lines right above, regex so, there will be no \r or \n in text anymore? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It will trim just There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see. so escaped char is retained but actually "\r\n" will be removed. okay. |
||
AssertEx.AreEqual(expectedOperationTree, actual); | ||
} | ||
|
||
#region Logging helpers | ||
|
||
private void LogCommonPropertiesAndNewLine(IOperation operation) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace Microsoft.CodeAnalysis.Test.Extensions | ||
{ | ||
public static class SemanticModelExtensions | ||
{ | ||
public static IOperation GetOperationInternal(this SemanticModel model, SyntaxNode node) | ||
{ | ||
return model.GetOperationInternal(node); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure what this extension method is trying to do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is it calling itself? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this is to bypass the feature flag check? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. GetOperationInternal is internal method on semantic model that by-passes the IOperation feature flag check. Only core TestUtilties have IVT, so this just exposes it to other test layers, such as ETA. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ah, got it. some comment would help :) since it looks like it is calling itself ha ha. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, sure. |
||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happen if tree has multiple nodes matching given type?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will behave similar to the compiler binding tests for those cases. Seems that this method adds the nodes from bottom up, and picks the first node that matches full text and is of expected type. Also I am not sure if we can have 2 nodes of same kind with same span in tree.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
but this test doesn't give in span? so it will just pick the first node in the tree?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The unit test generator will add comments denoting the span, like following:
bind
comments specify the span that the test helper will use.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See example unit test: https://github.com/dotnet/roslyn-internal/pull/1692#issuecomment-286570489
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah, I see, no span argument is needed since it will get those from source, got it.