From 1c00070857c7f2538b0662748c46976e1ecef1fe Mon Sep 17 00:00:00 2001 From: Matt Whitfield Date: Thu, 5 Oct 2023 19:27:34 +0100 Subject: [PATCH] Fix for Roslyn Syntax Tree Isolation (#234) --- .../Helpers/ISemanticModelLoader.cs | 9 +++++ .../Helpers/InvocationExtractor.cs | 11 +++--- .../Helpers/SemanticModelExtensions.cs | 34 +++++++++++++++++++ .../Helpers/SemanticModelLoaderProvider.cs | 7 ++++ src/Unitverse/Helper/TargetFinder.cs | 2 +- src/Unitverse/IUnitTestGeneratorPackage.cs | 3 +- src/Unitverse/UnitTestGeneratorPackage.cs | 12 +++++-- 7 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 src/Unitverse.Core/Helpers/ISemanticModelLoader.cs create mode 100644 src/Unitverse.Core/Helpers/SemanticModelLoaderProvider.cs diff --git a/src/Unitverse.Core/Helpers/ISemanticModelLoader.cs b/src/Unitverse.Core/Helpers/ISemanticModelLoader.cs new file mode 100644 index 00000000..158b73ea --- /dev/null +++ b/src/Unitverse.Core/Helpers/ISemanticModelLoader.cs @@ -0,0 +1,9 @@ +namespace Unitverse.Core.Helpers +{ + using Microsoft.CodeAnalysis; + + public interface ISemanticModelLoader + { + public SemanticModel GetSemanticModel(SyntaxNode node); + } +} diff --git a/src/Unitverse.Core/Helpers/InvocationExtractor.cs b/src/Unitverse.Core/Helpers/InvocationExtractor.cs index 21ff1e64..f83aefbb 100644 --- a/src/Unitverse.Core/Helpers/InvocationExtractor.cs +++ b/src/Unitverse.Core/Helpers/InvocationExtractor.cs @@ -2,7 +2,6 @@ { using System; using System.Collections.Generic; - using System.Xml.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -67,8 +66,8 @@ public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax no { if (_targetFields.Contains(fieldTarget)) { - var symbolInfo = _semanticModel.GetSymbolInfo(node); - if (symbolInfo.Symbol is IPropertySymbol propertySymbol) + var symbolInfo = _semanticModel.GetSymbolInfoSafe(node); + if (symbolInfo?.Symbol is IPropertySymbol propertySymbol) { if (!(node.Parent is AssignmentExpressionSyntax assignment) || node != assignment.Left) { @@ -90,8 +89,8 @@ public override void VisitInvocationExpression(InvocationExpressionSyntax node) { if (_targetFields.Contains(fieldTarget)) { - var symbolInfo = _semanticModel.GetSymbolInfo(node); - if (symbolInfo.Symbol is IMethodSymbol methodSymbol) + var symbolInfo = _semanticModel.GetSymbolInfoSafe(node); + if (symbolInfo?.Symbol is IMethodSymbol methodSymbol) { // if ReducedFrom is non-null, this is actually an extension method call if (methodSymbol.ReducedFrom is null) @@ -147,7 +146,7 @@ private bool GetFieldTarget(ExpressionSyntax expressionSyntax, out string fieldT private MethodDeclarationSyntax? GetInvokedMethodDeclaration(ExpressionSyntax invocationExpression) { - var symbol = _semanticModel.GetSymbolInfo(invocationExpression).Symbol; + var symbol = _semanticModel.GetSymbolInfoSafe(invocationExpression)?.Symbol; if (symbol == null || symbol.Kind != SymbolKind.Method diff --git a/src/Unitverse.Core/Helpers/SemanticModelExtensions.cs b/src/Unitverse.Core/Helpers/SemanticModelExtensions.cs index 2a49e4af..0480811d 100644 --- a/src/Unitverse.Core/Helpers/SemanticModelExtensions.cs +++ b/src/Unitverse.Core/Helpers/SemanticModelExtensions.cs @@ -20,5 +20,39 @@ public static class SemanticModelExtensions return symbol.ContainingType; } + + public static SymbolInfo? GetSymbolInfoSafe(this SemanticModel semanticModel, SyntaxNode node) + { + if (semanticModel.SyntaxTree == node.SyntaxTree) + { + return semanticModel.GetSymbolInfo(node); + } + + var loader = SemanticModelLoaderProvider.ModelLoader; + if (loader != null) + { + var model = loader.GetSemanticModel(node); + return model.GetSymbolInfo(node); + } + + return null; + } + + public static ISymbol? GetDeclaredSymbolSafe(this SemanticModel semanticModel, SyntaxNode node) + { + if (semanticModel.SyntaxTree == node.SyntaxTree) + { + return semanticModel.GetDeclaredSymbol(node); + } + + var loader = SemanticModelLoaderProvider.ModelLoader; + if (loader != null) + { + var model = loader.GetSemanticModel(node); + return model.GetDeclaredSymbol(node); + } + + return null; + } } } diff --git a/src/Unitverse.Core/Helpers/SemanticModelLoaderProvider.cs b/src/Unitverse.Core/Helpers/SemanticModelLoaderProvider.cs new file mode 100644 index 00000000..7f1b7080 --- /dev/null +++ b/src/Unitverse.Core/Helpers/SemanticModelLoaderProvider.cs @@ -0,0 +1,7 @@ +namespace Unitverse.Core.Helpers +{ + public static class SemanticModelLoaderProvider + { + public static ISemanticModelLoader? ModelLoader { get; set; } + } +} diff --git a/src/Unitverse/Helper/TargetFinder.cs b/src/Unitverse/Helper/TargetFinder.cs index 5ab4bccf..d297829d 100644 --- a/src/Unitverse/Helper/TargetFinder.cs +++ b/src/Unitverse/Helper/TargetFinder.cs @@ -96,7 +96,7 @@ private static async Task FindTargetByTypeAsync(ISymbol symbol, ProjectI if (firstType != null && !firstType.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword))) { - symbol = semanticModel.GetDeclaredSymbol(firstType); + symbol = semanticModel.GetDeclaredSymbolSafe(firstType); } } diff --git a/src/Unitverse/IUnitTestGeneratorPackage.cs b/src/Unitverse/IUnitTestGeneratorPackage.cs index b067fb00..9274d3ef 100644 --- a/src/Unitverse/IUnitTestGeneratorPackage.cs +++ b/src/Unitverse/IUnitTestGeneratorPackage.cs @@ -6,10 +6,11 @@ using Microsoft.VisualStudio.LanguageServices; using Microsoft.VisualStudio.Threading; using NuGet.VisualStudio; + using Unitverse.Core.Helpers; using Unitverse.Core.Options; using Unitverse.Core.Options.Editing; - public interface IUnitTestGeneratorPackage : IServiceProvider, IConfigurationWriter + public interface IUnitTestGeneratorPackage : IServiceProvider, IConfigurationWriter, ISemanticModelLoader { JoinableTaskFactory JoinableTaskFactory { get; } diff --git a/src/Unitverse/UnitTestGeneratorPackage.cs b/src/Unitverse/UnitTestGeneratorPackage.cs index 8235299d..0178c250 100644 --- a/src/Unitverse/UnitTestGeneratorPackage.cs +++ b/src/Unitverse/UnitTestGeneratorPackage.cs @@ -2,12 +2,10 @@ { using System; using System.Collections.Generic; - using System.IO; using System.Linq; using System.Runtime.InteropServices; - using System.Text; using System.Threading; - using System.Windows.Media.Animation; + using Microsoft.CodeAnalysis; using Microsoft.VisualStudio; using Microsoft.VisualStudio.ComponentModelHost; using Microsoft.VisualStudio.LanguageServices; @@ -16,6 +14,7 @@ using NuGet.VisualStudio; using Unitverse.Commands; using Unitverse.Core; + using Unitverse.Core.Helpers; using Unitverse.Core.Options; using Unitverse.Editor; using Unitverse.Options; @@ -148,6 +147,13 @@ protected override async Task InitializeAsync(CancellationToken cancellationToke await GenerateTestForSymbolCommand.InitializeAsync(this).ConfigureAwait(true); await GoToUnitTestsForSymbolCommand.InitializeAsync(this).ConfigureAwait(true); await CreateTestProjectCommand.InitializeAsync(this).ConfigureAwait(true); + + SemanticModelLoaderProvider.ModelLoader = this; + } + + public SemanticModel GetSemanticModel(SyntaxNode node) + { + return JoinableTaskFactory.Run(() => Workspace.CurrentSolution.GetDocument(node.SyntaxTree).GetSemanticModelAsync()); } } } \ No newline at end of file