提交 6fcd3675 编写于 作者: C Cyrus Najmabadi

C# impl done.

上级 5afc4286
......@@ -9,11 +9,11 @@
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.SemanticModelWorkspaceService
namespace Microsoft.CodeAnalysis.SemanticModelReuse
{
internal partial class SemanticModelWorkspaceServiceFactory : IWorkspaceServiceFactory
internal partial class SemanticModelReuseWorkspaceServiceFactory : IWorkspaceServiceFactory
{
private sealed class SemanticModelService : ISemanticModelService
private sealed class SemanticModelReuseWorkspaceService : ISemanticModelReuseWorkspaceService
{
public Task<SemanticModel> ReuseExistingSpeculativeModelAsync(Document document, SyntaxNode node, CancellationToken cancellationToken = default)
{
......
......@@ -1063,26 +1063,26 @@ public void Test()
await InsertText(code, textToInsert, expectDocumentAnalysis: true);
}
[Fact]
public void VBPropertyTest()
{
var markup = @"Class C
Default Public Property G(x As Integer) As Integer
Get
$$
End Get
Set(value As Integer)
End Set
End Property
End Class";
MarkupTestFile.GetPosition(markup, out var code, out int position);
var root = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseCompilationUnit(code);
var property = root.FindToken(position).Parent.FirstAncestorOrSelf<Microsoft.CodeAnalysis.VisualBasic.Syntax.PropertyBlockSyntax>();
var memberId = Microsoft.CodeAnalysis.VisualBasic.LanguageServices.VisualBasicSyntaxFacts.Instance.GetMethodLevelMemberId(root, property);
Assert.Equal(0, memberId);
}
// [Fact]
// public void VBPropertyTest()
// {
// var markup = @"Class C
// Default Public Property G(x As Integer) As Integer
// Get
// $$
// End Get
// Set(value As Integer)
// End Set
// End Property
//End Class";
// MarkupTestFile.GetPosition(markup, out var code, out int position);
// var root = Microsoft.CodeAnalysis.VisualBasic.SyntaxFactory.ParseCompilationUnit(code);
// var property = root.FindToken(position).Parent.FirstAncestorOrSelf<Microsoft.CodeAnalysis.VisualBasic.Syntax.PropertyBlockSyntax>();
// var memberId = Microsoft.CodeAnalysis.VisualBasic.LanguageServices.VisualBasicSyntaxFacts.Instance.GetMethodLevelMemberId(root, property);
// Assert.Equal(0, memberId);
// }
[Fact, WorkItem(739943, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/739943")]
public async Task SemanticChange_Propagation_Transitive()
......
......@@ -4,11 +4,11 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.SemanticModelReuse;
namespace Microsoft.CodeAnalysis.CSharp.SemanticModelReuse
......@@ -17,13 +17,152 @@ internal class CSharpSemanticModelReuseLanguageService : ISemanticModelReuseLang
{
public SyntaxNode? TryGetContainingMethodBodyForSpeculation(SyntaxNode node)
{
throw new NotImplementedException();
for (var current = node; current != null; current = current.Parent)
{
// These are the exact types that SemanticModel.TryGetSpeculativeSemanticModelForMethodBody accepts.
if (node is BaseMethodDeclarationSyntax baseMethod && baseMethod.Body != null)
return node;
if (node is AccessorDeclarationSyntax accessor && accessor.Body != null)
return node;
}
return null;
}
public Task<SemanticModel?> TryGetSpeculativeSemanticModelAsync(
public async Task<SemanticModel?> TryGetSpeculativeSemanticModelAsync(
SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken)
{
throw new NotImplementedException();
var previousRoot = await previousSemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var currentRoot = await currentBodyNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false);
var previousBodyNode = GetPreviousBodyNode(previousRoot, currentRoot, currentBodyNode);
if (previousBodyNode is BaseMethodDeclarationSyntax previousBaseMethod &&
currentBodyNode is BaseMethodDeclarationSyntax currentBaseMethod &&
previousBaseMethod.Body != null &&
previousSemanticModel.TryGetSpeculativeSemanticModelForMethodBody(previousBaseMethod.Body.SpanStart, currentBaseMethod, out var speculativeModel))
{
return speculativeModel;
}
if (previousBodyNode is AccessorDeclarationSyntax previousAccessorDeclaration &&
currentBodyNode is AccessorDeclarationSyntax currentAccessorDeclaration &&
previousAccessorDeclaration.Body != null &&
previousSemanticModel.TryGetSpeculativeSemanticModelForMethodBody(previousAccessorDeclaration.Body.SpanStart, currentAccessorDeclaration, out speculativeModel))
{
return speculativeModel;
}
return null;
}
private static SyntaxNode? GetPreviousBodyNode(SyntaxNode previousRoot, SyntaxNode currentRoot, SyntaxNode currentBodyNode)
{
var currentMembers = CSharpSyntaxFacts.Instance.GetMethodLevelMembers(currentRoot);
var index = currentMembers.IndexOf(currentBodyNode is AccessorDeclarationSyntax
? currentBodyNode.Parent!.Parent
: currentBodyNode);
if (index < 0)
{
Debug.Fail("Unhandled member type in GetPreviousBodyNode");
return null;
}
var previousMembers = CSharpSyntaxFacts.Instance.GetMethodLevelMembers(previousRoot);
if (currentMembers.Count != previousMembers.Count)
{
Debug.Fail("Member count shouldn't have changed as there were no top level edits.");
return null;
}
var previousBodyNode = previousMembers[index];
if (!(currentBodyNode is AccessorDeclarationSyntax currentAccessor))
return previousBodyNode;
// in the case of an accessor, have to find the previous accessor in the previous prop/event corresponding
// to the current prop/event.
var previousAccessorList = previousBodyNode switch
{
PropertyDeclarationSyntax previousProperty => previousProperty.AccessorList,
EventDeclarationSyntax previousEvent => previousEvent.AccessorList,
_ => null,
};
if (previousAccessorList == null)
{
Debug.Fail("Didn't find a corresponding accessor in the previous tree.");
return null;
}
var accessorIndex = ((AccessorListSyntax)currentAccessor.Parent!).Accessors.IndexOf(currentAccessor);
return previousAccessorList.Accessors[accessorIndex];
}
//public int GetMethodLevelMemberId(SyntaxNode root, SyntaxNode node)
//{
// Debug.Assert(root.SyntaxTree == node.SyntaxTree);
// var currentId = 0;
// Contract.ThrowIfFalse(TryGetMethodLevelMember(root, (n, i) => n == node, ref currentId, out var currentNode));
// Contract.ThrowIfFalse(currentId >= 0);
// CheckMemberId(root, node, currentId);
// return currentId;
//}
//public SyntaxNode GetMethodLevelMember(SyntaxNode root, int memberId)
//{
// var currentId = 0;
// if (!TryGetMethodLevelMember(root, (n, i) => i == memberId, ref currentId, out var currentNode))
// {
// return null;
// }
// Contract.ThrowIfNull(currentNode);
// CheckMemberId(root, currentNode, memberId);
// return currentNode;
//}
//private void GetMethodLevelMember(
// SyntaxNode node, Func<SyntaxNode, int, bool> predicate, out int resultId, out SyntaxNode? resultNode)
//{
// resultId = 0;
// resultNode = null;
// GetMethodLevelMemberWorker(node, predicate, ref resultId, ref resultNode);
// static GetMethodLevelMember(SyntaxNode node, Func<SyntaxNode, int, bool> predicate, ref int resultId, ref SyntaxNode? resultNode)
// {
// foreach (var member in node.)
// }
// foreach (var member in node.GetMembers())
// {
// if (IsTopLevelNodeWithMembers(member))
// {
// if (TryGetMethodLevelMember(member, predicate, ref currentId, out currentNode))
// {
// return true;
// }
// continue;
// }
// if (IsMethodLevelMember(member))
// {
// if (predicate(member, currentId))
// {
// currentNode = member;
// return true;
// }
// currentId++;
// }
// }
// currentNode = null;
// return false;
//}
}
}
......@@ -4,18 +4,11 @@
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
......@@ -42,7 +35,7 @@ private sealed class SemanticModelReuseWorkspaceService : ISemanticModelReuseWor
public async Task<SemanticModel> ReuseExistingSpeculativeModelAsync(
Document document, SyntaxNode node, CancellationToken cancellationToken)
{
var reuseService = document.GetRequiredLanguageService<ISemanticModelReuseService>();
var reuseService = document.GetRequiredLanguageService<ISemanticModelReuseLanguageService>();
// See if we're asking about a node actually in a method body. If so, see if we can reuse the
// existing semantic model. If not, return the current semantic model for the file.
......@@ -126,7 +119,7 @@ private sealed class SemanticModelReuseWorkspaceService : ISemanticModelReuseWor
if (cachedBodyNode == bodyNode)
return cachedSemanticModel;
var reuseService = document.GetRequiredLanguageService<ISemanticModelReuseService>();
var reuseService = document.GetRequiredLanguageService<ISemanticModelReuseLanguageService>();
return await reuseService.TryGetSpeculativeSemanticModelAsync(cachedSemanticModel, bodyNode, cancellationToken).ConfigureAwait(false);
}
......
......@@ -9,7 +9,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeCleanup;
using Microsoft.CodeAnalysis.CodeCleanup.Providers;
using Microsoft.CodeAnalysis.SemanticModelWorkspaceService;
using Microsoft.CodeAnalysis.SemanticModelReuse;
using Microsoft.CodeAnalysis.Test.Utilities;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Test.Utilities;
......@@ -230,8 +230,8 @@ End Class
var semanticModel = await document.GetSemanticModelAsync();
var root = await document.GetSyntaxRootAsync();
var accessor = root.DescendantNodes().OfType<VisualBasic.Syntax.AccessorBlockSyntax>().Last();
var factory = new SemanticModelWorkspaceServiceFactory();
var service = (ISemanticModelService)factory.CreateService(document.Project.Solution.Workspace.Services);
var factory = new SemanticModelReuseWorkspaceServiceFactory();
var service = (ISemanticModelReuseWorkspaceService)factory.CreateService(document.Project.Solution.Workspace.Services);
var newSemanticModel = await service.ReuseExistingSpeculativeModelAsync(document, accessor, CancellationToken.None);
Assert.NotNull(newSemanticModel);
var newDocument = CreateDocument(code, LanguageNames.VisualBasic);
......
......@@ -635,7 +635,8 @@ void goo()
var basemethod2 = tree2.FindTokenOnLeftOfPosition(position, CancellationToken.None).GetAncestor<CSharp.Syntax.BaseMethodDeclarationSyntax>();
var service = CSharp.CSharpSemanticFactsService.Instance;
var m = service.TryGetSpeculativeSemanticModel(firstModel, basemethod1, basemethod2, out var testModel);
var testModel = await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None);
Assert.True(testModel.IsSpeculativeSemanticModel);
var xSymbol = testModel.LookupSymbols(position).First(s => s.Name == "x");
......
......@@ -730,6 +730,7 @@ public bool IsMethodLevelMember(SyntaxNode node)
{
return node is BaseMethodDeclarationSyntax ||
node is BasePropertyDeclarationSyntax ||
node is EventDeclarationSyntax ||
node is EnumMemberDeclarationSyntax ||
node is BaseFieldDeclarationSyntax;
}
......@@ -978,71 +979,6 @@ public bool ContainsInMemberBody(SyntaxNode node, TextSpan span)
private static TextSpan GetBlockBodySpan(BlockSyntax body)
=> TextSpan.FromBounds(body.OpenBraceToken.Span.End, body.CloseBraceToken.SpanStart);
public int GetMethodLevelMemberId(SyntaxNode root, SyntaxNode node)
{
Debug.Assert(root.SyntaxTree == node.SyntaxTree);
var currentId = 0;
Contract.ThrowIfFalse(TryGetMethodLevelMember(root, (n, i) => n == node, ref currentId, out var currentNode));
Contract.ThrowIfFalse(currentId >= 0);
CheckMemberId(root, node, currentId);
return currentId;
}
public SyntaxNode GetMethodLevelMember(SyntaxNode root, int memberId)
{
var currentId = 0;
if (!TryGetMethodLevelMember(root, (n, i) => i == memberId, ref currentId, out var currentNode))
{
return null;
}
Contract.ThrowIfNull(currentNode);
CheckMemberId(root, currentNode, memberId);
return currentNode;
}
private bool TryGetMethodLevelMember(
SyntaxNode node, Func<SyntaxNode, int, bool> predicate, ref int currentId, out SyntaxNode currentNode)
{
foreach (var member in node.GetMembers())
{
if (IsTopLevelNodeWithMembers(member))
{
if (TryGetMethodLevelMember(member, predicate, ref currentId, out currentNode))
{
return true;
}
continue;
}
if (IsMethodLevelMember(member))
{
if (predicate(member, currentId))
{
currentNode = member;
return true;
}
currentId++;
}
}
currentNode = null;
return false;
}
[Conditional("DEBUG")]
private void CheckMemberId(SyntaxNode root, SyntaxNode node, int memberId)
{
var list = GetMethodLevelMembers(root);
var index = list.IndexOf(node);
Contract.ThrowIfFalse(index == memberId);
}
#nullable enable
public SyntaxNode? TryGetBindableParent(SyntaxToken token)
......
......@@ -1065,66 +1065,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Next
End Sub
Public Function GetMethodLevelMemberId(root As SyntaxNode, node As SyntaxNode) As Integer Implements ISyntaxFacts.GetMethodLevelMemberId
Debug.Assert(root.SyntaxTree Is node.SyntaxTree)
Dim currentId As Integer = Nothing
Dim currentNode As SyntaxNode = Nothing
Contract.ThrowIfFalse(TryGetMethodLevelMember(root, Function(n, i) n Is node, currentId, currentNode))
Contract.ThrowIfFalse(currentId >= 0)
CheckMemberId(root, node, currentId)
Return currentId
End Function
Public Function GetMethodLevelMember(root As SyntaxNode, memberId As Integer) As SyntaxNode Implements ISyntaxFacts.GetMethodLevelMember
Dim currentId As Integer = Nothing
Dim currentNode As SyntaxNode = Nothing
If Not TryGetMethodLevelMember(root, Function(n, i) i = memberId, currentId, currentNode) Then
Return Nothing
End If
Contract.ThrowIfNull(currentNode)
CheckMemberId(root, currentNode, memberId)
Return currentNode
End Function
Private Function TryGetMethodLevelMember(node As SyntaxNode, predicate As Func(Of SyntaxNode, Integer, Boolean), ByRef currentId As Integer, ByRef currentNode As SyntaxNode) As Boolean
For Each member In node.GetMembers()
If TypeOf member Is NamespaceBlockSyntax OrElse
TypeOf member Is TypeBlockSyntax OrElse
TypeOf member Is EnumBlockSyntax Then
If TryGetMethodLevelMember(member, predicate, currentId, currentNode) Then
Return True
End If
Continue For
End If
If IsMethodLevelMember(member) Then
If predicate(member, currentId) Then
currentNode = member
Return True
End If
currentId += 1
End If
Next
currentNode = Nothing
Return False
End Function
<Conditional("DEBUG")>
Private Sub CheckMemberId(root As SyntaxNode, node As SyntaxNode, memberId As Integer)
Dim list = GetMethodLevelMembers(root)
Dim index = list.IndexOf(node)
Contract.ThrowIfFalse(index = memberId)
End Sub
Public Function TryGetBindableParent(token As SyntaxToken) As SyntaxNode Implements ISyntaxFacts.TryGetBindableParent
Dim node = token.Parent
While node IsNot Nothing
......
......@@ -181,22 +181,6 @@ public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol)
public bool SupportsParameterizedProperties => false;
public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel)
{
Debug.Assert(oldNode.Kind() == newNode.Kind());
var model = oldSemanticModel;
if (!(oldNode is BaseMethodDeclarationSyntax oldMethod) || !(newNode is BaseMethodDeclarationSyntax newMethod) || oldMethod.Body == null)
{
speculativeModel = null;
return false;
}
var success = model.TryGetSpeculativeSemanticModelForMethodBody(oldMethod.Body.OpenBraceToken.Span.End, newMethod, out var csharpModel);
speculativeModel = csharpModel;
return success;
}
public ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken)
{
var original = model.GetOriginalSemanticModel();
......
......@@ -12,7 +12,7 @@
using Microsoft.CodeAnalysis.GeneratedCodeRecognition;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.SemanticModelWorkspaceService;
using Microsoft.CodeAnalysis.SemanticModelReuse;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
......@@ -131,8 +131,9 @@ public static Task<SemanticModel> ReuseExistingSpeculativeModelAsync(this Docume
private static Task<SemanticModel> ReuseExistingSpeculativeModelAsync(
Document document, SyntaxNode node, TextSpan span, CancellationToken cancellationToken)
{
var workspace = document.Project.Solution.Workspace;
var syntaxFactService = document.GetRequiredLanguageService<ISyntaxFactsService>();
var semanticModelService = document.Project.Solution.Workspace.Services.GetRequiredService<ISemanticModelService>();
var semanticModelService = workspace.Services.GetRequiredService<ISemanticModelReuseWorkspaceService>();
// check whether given span is a valid span to do speculative binding
var speculativeBindingSpan = syntaxFactService.GetMemberBodySpanForSpeculativeBinding(node);
......
......@@ -200,42 +200,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Get
End Property
Public Function TryGetSpeculativeSemanticModel(oldSemanticModel As SemanticModel, oldNode As SyntaxNode, newNode As SyntaxNode, <Out> ByRef speculativeModel As SemanticModel) As Boolean Implements ISemanticFactsService.TryGetSpeculativeSemanticModel
Debug.Assert(oldNode.Kind = newNode.Kind)
Dim model = oldSemanticModel
' currently we only support method. field support will be added later.
Dim oldMethod = TryCast(oldNode, MethodBlockBaseSyntax)
Dim newMethod = TryCast(newNode, MethodBlockBaseSyntax)
If oldMethod Is Nothing OrElse newMethod Is Nothing Then
speculativeModel = Nothing
Return False
End If
' No method body?
If oldMethod.Statements.IsEmpty AndAlso oldMethod.EndBlockStatement.IsMissing Then
speculativeModel = Nothing
Return False
End If
Dim position As Integer
If model.IsSpeculativeSemanticModel Then
' Chaining speculative semantic model is not supported, use the original model.
position = model.OriginalPositionForSpeculation
model = model.ParentModel
Contract.ThrowIfNull(model)
Contract.ThrowIfTrue(model.IsSpeculativeSemanticModel)
Else
position = oldMethod.BlockStatement.FullSpan.End
End If
Dim vbSpeculativeModel As SemanticModel = Nothing
Dim success = model.TryGetSpeculativeSemanticModelForMethodBody(position, newMethod, vbSpeculativeModel)
speculativeModel = vbSpeculativeModel
Return success
End Function
Public Function GetAliasNameSet(model As SemanticModel, cancellationToken As CancellationToken) As ImmutableHashSet(Of String) Implements ISemanticFactsService.GetAliasNameSet
Dim original = DirectCast(model.GetOriginalSemanticModel(), SemanticModel)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册