diff --git a/.editorconfig b/.editorconfig index d37bb6d5faa99..b2c5d02ecaeae 100644 --- a/.editorconfig +++ b/.editorconfig @@ -221,9 +221,6 @@ csharp_preserve_single_line_statements = true # warning RS0037: PublicAPI.txt is missing '#nullable enable' dotnet_diagnostic.RS0037.severity = none -# RS0046: Avoid the 'Opt' suffix -dotnet_diagnostic.RS0046.severity = suggestion - [src/CodeStyle/**.{cs,vb}] # warning RS0005: Do not use generic CodeAction.Create to create CodeAction dotnet_diagnostic.RS0005.severity = none diff --git a/eng/Versions.props b/eng/Versions.props index a6f69b3cdd0c1..b813c48c77d1e 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -27,8 +27,8 @@ - 3.3.0-beta1.20351.1 - 3.3.0-beta1.20351.1 + 3.3.0-beta1.20352.4 + 3.3.0-beta1.20352.4 3.6.0-2.final 1.0.1-beta1.20210.2 3.7.0-3.20271.4 diff --git a/eng/config/rulesets/Shipping.ruleset b/eng/config/rulesets/Shipping.ruleset index 21007bb5c5a59..cda6d034d71a8 100644 --- a/eng/config/rulesets/Shipping.ruleset +++ b/eng/config/rulesets/Shipping.ruleset @@ -50,6 +50,8 @@ + + diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs index 01d679d882f7f..d985ed14794d1 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.InfoCache.cs @@ -45,7 +45,7 @@ public InfoCache(Compilation compilation) { var substringMethod = stringType.GetMembers(nameof(string.Substring)) .OfType() - .FirstOrDefault(m => IsSliceLikeMethod(m)); + .FirstOrDefault(m => IsTwoArgumentSliceLikeMethod(m)); _methodToMemberInfo[substringMethod] = ComputeMemberInfo(substringMethod, requireRangeMember: false); } @@ -54,12 +54,12 @@ public InfoCache(Compilation compilation) private static IMethodSymbol GetSliceLikeMethod(INamedTypeSymbol namedType) => namedType.GetMembers() .OfType() - .Where(m => IsSliceLikeMethod(m)) + .Where(m => IsTwoArgumentSliceLikeMethod(m)) .FirstOrDefault(); public bool TryGetMemberInfo(IMethodSymbol method, out MemberInfo memberInfo) { - if (!IsSliceLikeMethod(method)) + if (!IsTwoArgumentSliceLikeMethod(method)) { memberInfo = default; return false; @@ -69,9 +69,44 @@ public bool TryGetMemberInfo(IMethodSymbol method, out MemberInfo memberInfo) return memberInfo.LengthLikeProperty != null; } + public bool TryGetMemberInfoOneArgument(IMethodSymbol method, out MemberInfo memberInfo) + { + if (!IsOneArgumentSliceLikeMethod(method)) + { + memberInfo = default; + return false; + } + + if (!_methodToMemberInfo.TryGetValue(method, out memberInfo)) + { + // Find overload of our method that is a slice-like method with two arguments. + // Computing member info for this method will also check that the containing type + // has an int32 'Length' or 'Count' property, and has a suitable indexer, + // so we don't have to. + var overloadWithTwoArguments = method.ContainingType + .GetMembers(method.Name) + .OfType() + .FirstOrDefault(s => IsTwoArgumentSliceLikeMethod(s)); + if (overloadWithTwoArguments is null) + { + memberInfo = default; + return false; + } + + // Since the search is expensive, we keep both the original one-argument and + // two-arguments overload as keys in the cache, pointing to the same + // member information object. + var newMemberInfo = _methodToMemberInfo.GetOrAdd(overloadWithTwoArguments, _ => ComputeMemberInfo(overloadWithTwoArguments, requireRangeMember: true)); + _methodToMemberInfo.GetOrAdd(method, _ => newMemberInfo); + memberInfo = newMemberInfo; + } + + return memberInfo.LengthLikeProperty != null; + } + private MemberInfo ComputeMemberInfo(IMethodSymbol sliceLikeMethod, bool requireRangeMember) { - Debug.Assert(IsSliceLikeMethod(sliceLikeMethod)); + Debug.Assert(IsTwoArgumentSliceLikeMethod(sliceLikeMethod)); // Check that the type has an int32 'Length' or 'Count' property. If not, we don't // consider it something indexable. @@ -106,7 +141,7 @@ private MemberInfo ComputeMemberInfo(IMethodSymbol sliceLikeMethod, bool require var actualSliceMethod = sliceLikeMethod.ContainingType.GetMembers(nameof(Span.Slice)) .OfType() - .FirstOrDefault(s => IsSliceLikeMethod(s)); + .FirstOrDefault(s => IsTwoArgumentSliceLikeMethod(s)); if (actualSliceMethod != null) { return new MemberInfo(lengthLikeProperty, overloadedMethodOpt: null); diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs index babff3a37a7e3..02050fcc995b7 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.Result.cs @@ -2,6 +2,8 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +#nullable enable + using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; @@ -12,7 +14,7 @@ internal partial class CSharpUseRangeOperatorDiagnosticAnalyzer { public enum ResultKind { - // like s.Substring(expr, s.Length - expr). 'expr' has to match on both sides. + // like s.Substring(expr, s.Length - expr) or s.Substring(expr). 'expr' has to match on both sides. Computed, // like s.Substring(constant1, s.Length - constant2). the constants don't have to match. @@ -28,13 +30,17 @@ public readonly struct Result public readonly IMethodSymbol SliceLikeMethod; public readonly MemberInfo MemberInfo; public readonly IOperation Op1; - public readonly IOperation Op2; + + /// + /// Can be null, if we are dealing with one-argument call to a slice-like method. + /// + public readonly IOperation? Op2; public Result( ResultKind kind, CodeStyleOption2 option, IInvocationOperation invocationOperation, InvocationExpressionSyntax invocation, IMethodSymbol sliceLikeMethod, MemberInfo memberInfo, - IOperation op1, IOperation op2) + IOperation op1, IOperation? op2) { Kind = kind; Option = option; diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs index 12b1e496a25ce..c87de1349f35d 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/CSharpUseRangeOperatorDiagnosticAnalyzer.cs @@ -111,13 +111,54 @@ private void AnalyzeInvocation( } } - // look for `s.Slice(e1, end - e2)` - if (invocation.Instance is null || - invocation.Arguments.Length != 2) + // look for `s.Slice(e1, end - e2)` or `s.Slice(e1)` + if (invocation.Instance is null) { return null; } + return invocation.Arguments.Length switch + { + 1 => AnalyzeOneArgumentInvocation(invocation, infoCache, invocationSyntax, option), + 2 => AnalyzeTwoArgumentInvocation(invocation, infoCache, invocationSyntax, option), + _ => null, + }; + } + + private static Result? AnalyzeOneArgumentInvocation( + IInvocationOperation invocation, + InfoCache infoCache, + InvocationExpressionSyntax invocationSyntax, + CodeStyleOption2 option) + { + var targetMethod = invocation.TargetMethod; + + // We are dealing with a call like `.Substring(expr)`. + // Ensure that there is an overload with signature like `Substring(int start, int length)` + // and there is a suitable indexer to replace this with `[expr..]`. + if (!infoCache.TryGetMemberInfoOneArgument(targetMethod, out var memberInfo)) + { + return null; + } + + var startOperation = invocation.Arguments[0].Value; + return new Result( + ResultKind.Computed, + option, + invocation, + invocationSyntax, + targetMethod, + memberInfo, + op1: startOperation, + op2: null); // The range will run to the end. + } + + private static Result? AnalyzeTwoArgumentInvocation( + IInvocationOperation invocation, + InfoCache infoCache, + InvocationExpressionSyntax invocationSyntax, + CodeStyleOption2 option) + { // See if the call is to something slice-like. var targetMethod = invocation.TargetMethod; diff --git a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs index 7152b41006ecd..a21c93305c7d8 100644 --- a/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs +++ b/src/Analyzers/CSharp/Analyzers/UseIndexOrRangeOperator/Helpers.cs @@ -80,16 +80,35 @@ public static bool IsIntIndexingMethod(IMethodSymbol method) /// names of the parameters are checked to ensure they are appropriate slice-like. These /// names were picked by examining the patterns in the BCL for slicing members. /// - public static bool IsSliceLikeMethod(IMethodSymbol method) - => method != null && - IsPublicInstance(method) && - method.Parameters.Length == 2 && - // From: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/ranges.md#decisions-made-during-implementation - // - // When looking for the pattern members, we look for original definitions, not - // constructed members - IsSliceFirstParameter(method.OriginalDefinition.Parameters[0]) && - IsSliceSecondParameter(method.OriginalDefinition.Parameters[1]); + public static bool IsTwoArgumentSliceLikeMethod(IMethodSymbol method) + { + // From: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/ranges.md#decisions-made-during-implementation + // + // When looking for the pattern members, we look for original definitions, not + // constructed members + return method != null && + IsPublicInstance(method) && + method.Parameters.Length == 2 && + IsSliceFirstParameter(method.OriginalDefinition.Parameters[0]) && + IsSliceSecondParameter(method.OriginalDefinition.Parameters[1]); + } + + /// + /// Look for methods like "SomeType MyType.Slice(int start)". Note that the + /// name of the parameter is checked to ensure it is appropriate slice-like. + /// This name was picked by examining the patterns in the BCL for slicing members. + /// + public static bool IsOneArgumentSliceLikeMethod(IMethodSymbol method) + { + // From: https://github.com/dotnet/csharplang/blob/master/proposals/csharp-8.0/ranges.md#decisions-made-during-implementation + // + // When looking for the pattern members, we look for original definitions, not + // constructed members + return method != null && + IsPublicInstance(method) && + method.Parameters.Length == 1 && + IsSliceFirstParameter(method.OriginalDefinition.Parameters[0]); + } private static bool IsSliceFirstParameter(IParameterSymbol parameter) => parameter.Type.SpecialType == SpecialType.System_Int32 && diff --git a/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs index d48d9b8523300..d0b5cebee6f7f 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseIndexOrRangeOperator/CSharpUseRangeOperatorCodeFixProvider.cs @@ -150,16 +150,22 @@ private static RangeExpressionSyntax CreateComputedRange(Result result) var startFromEnd = IsFromEnd(lengthLikeProperty, instance, ref startOperation); var startExpr = (ExpressionSyntax)startOperation.Syntax; - // Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just - // change our end-op to be `val` and record that we should emit it as `^val`. - var endFromEnd = IsFromEnd(lengthLikeProperty, instance, ref endOperation); - var endExpr = (ExpressionSyntax)endOperation.Syntax; - - // If the range operation goes to 'expr.Length' then we can just leave off the end part - // of the range. i.e. `start..` - if (IsInstanceLengthCheck(lengthLikeProperty, instance, endOperation)) + var endFromEnd = false; + ExpressionSyntax endExpr = null; + + if (!(endOperation is null)) { - endExpr = null; + // We need to do the same for the second argument, since it's present. + // Similarly, if our end-op is actually equivalent to `expr.Length - val`, then just + // change our end-op to be `val` and record that we should emit it as `^val`. + endFromEnd = IsFromEnd(lengthLikeProperty, instance, ref endOperation); + + // Check if the range goes to 'expr.Length'; if it does, we leave off + // the end part of the range, i.e. `start..`. + if (!IsInstanceLengthCheck(lengthLikeProperty, instance, endOperation)) + { + endExpr = (ExpressionSyntax)endOperation.Syntax; + } } // If we're starting the range operation from 0, then we can just leave off the start of diff --git a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs index 9a594c1c376c8..bfbbba16f43b4 100644 --- a/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs +++ b/src/Analyzers/CSharp/Tests/UseIndexOrRangeOperator/UseRangeOperatorTests.cs @@ -204,6 +204,98 @@ void Goo(string s, int bar, int baz) }.RunAsync(); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestSubstringOneArgument() + { + var source = +@" +class C +{ + void Goo(string s) + { + var v = s.Substring([|1|]); + } +}"; + var fixedSource = +@" +class C +{ + void Goo(string s) + { + var v = s[1..]; + } +}"; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestSliceOneArgument() + { + var source = +@" +using System; +class C +{ + void Goo(Span s) + { + var v = s.Slice([|1|]); + } +}"; + var fixedSource = +@" +using System; +class C +{ + void Goo(Span s) + { + var v = s[1..]; + } +}"; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestExpressionOneArgument() + { + var source = +@" +class C +{ + void Goo(string s, int bar) + { + var v = s.Substring([|bar|]); + } +}"; + var fixedSource = +@" +class C +{ + void Goo(string s, int bar) + { + var v = s[bar..]; + } +}"; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestConstantSubtraction1() { @@ -416,6 +508,38 @@ void Goo(string s, string t) }.RunAsync(); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestFixAllInvocationToElementAccess2() + { + // Note: once the IOp tree has support for range operators, this should + // simplify even further. + var source = +@" +class C +{ + void Goo(string s, string t) + { + var v = t.Substring([|s.Substring([|1|])[0]|]); + } +}"; + var fixedSource = +@" +class C +{ + void Goo(string s, string t) + { + var v = t[s[1..][0]..]; + } +}"; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] public async Task TestWithTypeWithActualSliceMethod1() { @@ -479,5 +603,37 @@ void Goo(Span s) FixedCode = fixedSource, }.RunAsync(); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseRangeOperator)] + public async Task TestWithTypeWithActualSliceMethod3() + { + var source = +@" +using System; +class C +{ + void Goo(Span s) + { + var v = s.Slice([|1|]); + } +}"; + var fixedSource = +@" +using System; +class C +{ + void Goo(Span s) + { + var v = s[1..]; + } +}"; + + await new VerifyCS.Test + { + ReferenceAssemblies = ReferenceAssemblies.NetCore.NetCoreApp31, + TestCode = source, + FixedCode = fixedSource, + }.RunAsync(); + } } } diff --git a/src/CodeStyle/Core/CodeFixes/LanguageServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs b/src/CodeStyle/Core/CodeFixes/LanguageServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs index eb393456d0f14..a3e57e222dbfe 100644 --- a/src/CodeStyle/Core/CodeFixes/LanguageServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs +++ b/src/CodeStyle/Core/CodeFixes/LanguageServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs @@ -9,13 +9,17 @@ 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 GetSemanticModelForNodeAsync(Document document, SyntaxNode node, CancellationToken cancellationToken = default) + public SemanticModelReuseWorkspaceService(Workspace _) + { + } + + public Task ReuseExistingSpeculativeModelAsync(Document document, SyntaxNode node, CancellationToken cancellationToken) { // TODO: port the GetSemanticModelForNodeAsync implementation from Workspaces layer, // which currently relies on a bunch of internal APIs. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs index e9ac626629dc1..c7d51f7d17ba1 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_WithExpression.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -25,8 +25,6 @@ private BoundExpression BindWithExpression(WithExpressionSyntax syntax, Diagnost var receiverType = receiver.Type; var lookupResult = LookupResult.GetInstance(); - HashSet? useSiteDiagnostics = null; - bool hasErrors = false; if (receiverType is null || receiverType.IsVoidType()) @@ -38,6 +36,8 @@ private BoundExpression BindWithExpression(WithExpressionSyntax syntax, Diagnost MethodSymbol? cloneMethod = null; if (!receiverType.IsErrorType()) { + HashSet? useSiteDiagnostics = null; + LookupMembersInType( lookupResult, receiverType, @@ -69,10 +69,11 @@ private BoundExpression BindWithExpression(WithExpressionSyntax syntax, Diagnost TypeCompareKind.ConsiderEverything, ref useSiteDiagnostics)) { - useSiteDiagnostics = null; hasErrors = true; diagnostics.Add(ErrorCode.ERR_NoSingleCloneMethod, syntax.Expression.Location, receiverType); } + + diagnostics.Add(syntax.Expression, useSiteDiagnostics); } var initializer = BindInitializerExpression( diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs index abc9837d9a8af..9d21cba368a34 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs @@ -128055,5 +128055,26 @@ class B : A Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "x").WithArguments("System.Nullable", "T", "T").WithLocation(11, 47) ); } + + [Fact] + [WorkItem(44348, "https://github.com/dotnet/roslyn/issues/44348")] + public void Constraints() + { + var source = +@"class A +{ + internal interface IA { } + internal class C { } +} +#nullable enable +class B : A + where T : B.IB + where U : A.C +{ + internal interface IB : IA { } +}"; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics(); + } } } diff --git a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs index 0568eabc6de79..49feb2b198973 100644 --- a/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs +++ b/src/Compilers/Core/Portable/PEWriter/ReferenceIndexerBase.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using Roslyn.Utilities; using EmitContext = Microsoft.CodeAnalysis.Emit.EmitContext; using Microsoft.CodeAnalysis.Emit; @@ -13,8 +12,8 @@ namespace Microsoft.Cci { internal abstract class ReferenceIndexerBase : MetadataVisitor { - private readonly HashSet _alreadySeen = new HashSet(); - private readonly HashSet _alreadyHasToken = new HashSet(); + private readonly HashSet _alreadySeen = new HashSet(MetadataEntityReferenceComparer.ConsiderEverything); + private readonly HashSet _alreadyHasToken = new HashSet(MetadataEntityReferenceComparer.ConsiderEverything); protected bool typeReferenceNeedsToken; internal ReferenceIndexerBase(EmitContext context) diff --git a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs index f3e931148d9a8..fa884c717e1d0 100644 --- a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs +++ b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs @@ -34,11 +34,11 @@ protected override async Task TestWorkerAsync(Document document, TextSpan textSp if (mode == TestMode.Position) { int position = node?.SpanStart ?? textSpan.Start; - inferredType = typeInference.InferType(await document.GetSemanticModelForSpanAsync(new TextSpan(position, 0), CancellationToken.None), position, objectAsDefault: true, cancellationToken: CancellationToken.None); + inferredType = typeInference.InferType(await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None), position, objectAsDefault: true, cancellationToken: CancellationToken.None); } else { - inferredType = typeInference.InferType(await document.GetSemanticModelForSpanAsync(node?.Span ?? textSpan, CancellationToken.None), node, objectAsDefault: true, cancellationToken: CancellationToken.None); + inferredType = typeInference.InferType(await document.ReuseExistingSpeculativeModelAsync(node?.Span ?? textSpan, CancellationToken.None), node, objectAsDefault: true, cancellationToken: CancellationToken.None); } var typeSyntax = inferredType.GenerateTypeSyntax().NormalizeWhitespace(); diff --git a/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx b/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx index abc628fc3e094..2c958de5434a4 100644 --- a/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx +++ b/src/EditorFeatures/Core.Wpf/EditorFeaturesWpfResources.resx @@ -117,15 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Downloading index failed - - - Downloading index failed:{0} - - - Downloading IntelliSense index for {0} - Regex - Comment diff --git a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj index de3d32ed6966c..4636b9e831ff8 100644 --- a/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj +++ b/src/EditorFeatures/Core.Wpf/Microsoft.CodeAnalysis.EditorFeatures.Wpf.csproj @@ -36,7 +36,6 @@ - diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf index bb9e67c0272d2..a320dfd4df323 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.cs.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Index se nepodařilo stáhnout. - - - - Downloading index failed:{0} - Index se nepodařilo stáhnout: {0}. - - - - Downloading IntelliSense index for {0} - Stahuje se index IntelliSense pro {0}. - - Gathering Suggestions - '{0}' Shromažďují se návrhy – {0}. diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf index 3f7c87d97cbcb..dcd0f0cb5d55e 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.de.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Fehler beim Herunterladen des Index. - - - - Downloading index failed:{0} - Fehler beim Herunterladen von Index: {0}. - - - - Downloading IntelliSense index for {0} - Der IntelliSense-Index für "{0}" wird heruntergeladen. - - Gathering Suggestions - '{0}' Vorschläge werden gesammelt: "{0}" diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf index 471a76b0c3ed8..6681289bdb553 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.es.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Error al descargar el índice. - - - - Downloading index failed:{0} - Error al descargar el índice: {0} - - - - Downloading IntelliSense index for {0} - Descargando el índice de IntelliSense para {0} - - Gathering Suggestions - '{0}' Recopilando sugerencias: "{0}" diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf index d4e0173209e30..f0498189ae43f 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.fr.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Échec du téléchargement de l'index - - - - Downloading index failed:{0} - Échec du téléchargement de l'index : {0} - - - - Downloading IntelliSense index for {0} - Téléchargement de l'index IntelliSense pour {0} - - Gathering Suggestions - '{0}' Collecte des suggestions - '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf index 1e2002a8d32b2..4c8d215fe9e38 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.it.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Il download dell'indice non è riuscito - - - - Downloading index failed:{0} - Il download dell'indice non è riuscito: {0} - - - - Downloading IntelliSense index for {0} - Download dell'indice IntelliSense per {0} - - Gathering Suggestions - '{0}' Raccolta dei suggerimenti - '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf index a8df419a35d19..0759a50f49c6e 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ja.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - インデックスのダウンロードに失敗しました - - - - Downloading index failed:{0} - インデックスのダウンロードに失敗しました: {0} - - - - Downloading IntelliSense index for {0} - {0} の IntelliSense インデックスをダウンロードしています - - Gathering Suggestions - '{0}' 提案を収集しています - '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf index 42704a0816ae5..b5a405dbbd2d7 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ko.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - 인덱스를 다운로드하지 못했습니다. - - - - Downloading index failed:{0} - 인덱스 다운로드 실패:{0} - - - - Downloading IntelliSense index for {0} - {0}의 IntelliSense 인덱스 다운로드 중 - - Gathering Suggestions - '{0}' 제안을 수집하는 중 - '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf index b6f3b62060d0c..4a4f9eabad93e 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pl.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Pobieranie indeksu nie powiodło się - - - - Downloading index failed:{0} - Pobieranie indeksu nie powiodło się: {0} - - - - Downloading IntelliSense index for {0} - Pobieranie indeksu funkcji IntelliSense dla {0} - - Gathering Suggestions - '{0}' Zbieranie sugestii — „{0}” diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf index fddad3a55c4a5..f3e2df09e2dc7 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.pt-BR.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Falha ao baixar o índice - - - - Downloading index failed:{0} - Falha ao baixar o índice:{0} - - - - Downloading IntelliSense index for {0} - Baixando o índice do IntelliSense para {0} - - Gathering Suggestions - '{0}' Obtendo Sugestões – '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf index 4ba9de4639073..3938a53aafcc0 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.ru.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Не удалось скачать индекс. - - - - Downloading index failed:{0} - Ошибка при скачивании индекса: {0} - - - - Downloading IntelliSense index for {0} - Загрузка индекса IntelliSense для {0} - - Gathering Suggestions - '{0}' Сбор предложений — "{0}" diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf index 029aa377b2aaa..ca7e419cc9085 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.tr.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - Dizin indirilemedi - - - - Downloading index failed:{0} - Dizin indirilemedi: {0} - - - - Downloading IntelliSense index for {0} - {0} için IntelliSense dizini indiriliyor - - Gathering Suggestions - '{0}' Öneriler Toplanıyor - '{0}' diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf index d20299b06e713..f1ec7ece9514b 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hans.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - 下载索引失败 - - - - Downloading index failed:{0} - 下载索引失败: {0} - - - - Downloading IntelliSense index for {0} - 正在下载用于 {0} 的 IntelliSense 索引 - - Gathering Suggestions - '{0}' 正在收集建议 -“{0}” diff --git a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf index a11b02105d5fe..f4e3f0bc4d300 100644 --- a/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core.Wpf/xlf/EditorFeaturesWpfResources.zh-Hant.xlf @@ -2,21 +2,6 @@ - - Downloading index failed - 下載索引失敗 - - - - Downloading index failed:{0} - 下載索引失敗: {0} - - - - Downloading IntelliSense index for {0} - 正在為 {0} 下載 IntelliSense 索引 - - Gathering Suggestions - '{0}' 正在蒐集建議 - '{0}' diff --git a/src/EditorFeatures/Core/EditorFeaturesResources.resx b/src/EditorFeatures/Core/EditorFeaturesResources.resx index 5961c302c7b88..01a58965d7c7e 100644 --- a/src/EditorFeatures/Core/EditorFeaturesResources.resx +++ b/src/EditorFeatures/Core/EditorFeaturesResources.resx @@ -936,4 +936,13 @@ Do you want to proceed? (external) + + Downloading index failed + + + Downloading index failed:{0} + + + Downloading IntelliSense index for {0} + \ No newline at end of file diff --git a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs index 5f228dd18cd03..f03826154e6e0 100644 --- a/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs +++ b/src/EditorFeatures/Core/Implementation/RenameTracking/RenameTrackingTaggerProvider.TrackingSession.cs @@ -170,7 +170,7 @@ private async Task DetermineIfRenamableIdentifierAsync(Sn var languageHeuristicsService = document.GetLanguageService(); if (syntaxFactsService.IsIdentifier(token) && languageHeuristicsService.IsIdentifierValidForRenameTracking(token.Text)) { - var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, _cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(token.Parent, _cancellationToken).ConfigureAwait(false); var semanticFacts = document.GetLanguageService(); var renameSymbolInfo = RenameUtilities.GetTokenRenameInfo(semanticFacts, semanticModel, token, _cancellationToken); diff --git a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj index c02237181fc45..d1a791db1db94 100644 --- a/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj +++ b/src/EditorFeatures/Core/Microsoft.CodeAnalysis.EditorFeatures.csproj @@ -28,7 +28,9 @@ + + @@ -44,7 +46,6 @@ - diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IAddReferenceDatabaseWrapper.cs b/src/EditorFeatures/Core/SymbolSearch/IAddReferenceDatabaseWrapper.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IAddReferenceDatabaseWrapper.cs rename to src/EditorFeatures/Core/SymbolSearch/IAddReferenceDatabaseWrapper.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IDatabaseFactoryService.cs b/src/EditorFeatures/Core/SymbolSearch/IDatabaseFactoryService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IDatabaseFactoryService.cs rename to src/EditorFeatures/Core/SymbolSearch/IDatabaseFactoryService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IDelayService.cs b/src/EditorFeatures/Core/SymbolSearch/IDelayService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IDelayService.cs rename to src/EditorFeatures/Core/SymbolSearch/IDelayService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IIOService.cs b/src/EditorFeatures/Core/SymbolSearch/IIOService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IIOService.cs rename to src/EditorFeatures/Core/SymbolSearch/IIOService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IPatchService.cs b/src/EditorFeatures/Core/SymbolSearch/IPatchService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IPatchService.cs rename to src/EditorFeatures/Core/SymbolSearch/IPatchService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/IRemoteControlService.cs b/src/EditorFeatures/Core/SymbolSearch/IRemoteControlService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/IRemoteControlService.cs rename to src/EditorFeatures/Core/SymbolSearch/IRemoteControlService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/Patching/Delta.cs b/src/EditorFeatures/Core/SymbolSearch/Patching/Delta.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/Patching/Delta.cs rename to src/EditorFeatures/Core/SymbolSearch/Patching/Delta.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.DatabaseFactoryService.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.DatabaseFactoryService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.DatabaseFactoryService.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.DatabaseFactoryService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.DelayService.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.DelayService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.DelayService.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.DelayService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.IOService.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.IOService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.IOService.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.IOService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.PatchService.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.PatchService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.PatchService.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.PatchService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.RemoteControlService.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.RemoteControlService.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.RemoteControlService.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.RemoteControlService.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs similarity index 99% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs index a27ac3e6f2ad7..a8b312660f9c6 100644 --- a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.Update.cs +++ b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.Update.cs @@ -232,7 +232,7 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo { try { - var title = string.Format(EditorFeaturesWpfResources.Downloading_IntelliSense_index_for_0, _source); + var title = string.Format(EditorFeaturesResources.Downloading_IntelliSense_index_for_0, _source); await _service._progressService.OnDownloadFullDatabaseStartedAsync(title).ConfigureAwait(false); var (succeeded, delay) = await DownloadFullDatabaseWorkerAsync(databaseFileInfo, cancellationToken).ConfigureAwait(false); @@ -243,7 +243,7 @@ private async Task DownloadFullDatabaseAsync(FileInfo databaseFileInfo else { await _service._progressService.OnDownloadFullDatabaseFailedAsync( - EditorFeaturesWpfResources.Downloading_index_failed).ConfigureAwait(false); + EditorFeaturesResources.Downloading_index_failed).ConfigureAwait(false); } return delay; @@ -256,7 +256,7 @@ await _service._progressService.OnDownloadFullDatabaseFailedAsync( catch (Exception e) { var message = string.Format( - EditorFeaturesWpfResources.Downloading_index_failed_0, + EditorFeaturesResources.Downloading_index_failed_0, "\r\n" + e.ToString()); await _service._progressService.OnDownloadFullDatabaseFailedAsync(message).ConfigureAwait(false); throw; diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngine.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngine.cs diff --git a/src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngineFactory.cs b/src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs similarity index 100% rename from src/EditorFeatures/Core.Wpf/SymbolSearch/SymbolSearchUpdateEngineFactory.cs rename to src/EditorFeatures/Core/SymbolSearch/SymbolSearchUpdateEngineFactory.cs diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf index 4b738a4c99a62..bcb3a82769c35 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.cs.xlf @@ -37,6 +37,21 @@ Chcete přesto pokračovat? Výsledkem může být poškozený kód. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces položky z neimportovaných oborů názvů diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf index ee7484efb840d..6cdbed9d656fe 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.de.xlf @@ -37,6 +37,21 @@ Möchten Sie dennoch fortfahren? Möglicherweise wird fehlerhafter Code generiert. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces Elemente aus nicht importierten Namespaces diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf index b15597c411882..d5719a6be8979 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.es.xlf @@ -37,6 +37,21 @@ ¿Desea continuar? Esto puede producir código roto. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces elementos de espacios de nombres no importados diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf index 6ebde0fb6b99d..7ea2133c7a541 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.fr.xlf @@ -37,6 +37,21 @@ Voulez-vous continuer ? Le code sera interrompu. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces éléments provenant d'espaces de noms non importés diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf index 9a99bea0dc672..c1f67141e97d0 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.it.xlf @@ -37,6 +37,21 @@ Procedere comunque? Il codice ottenuto potrebbe essere danneggiato. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces elementi da spazi dei nomi non importati diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf index 3764650d39c4a..c813915ddde08 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ja.xlf @@ -37,6 +37,21 @@ 続行しますか。破損状態のコードが生成される可能性があります。 + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces インポートされていない名前空間の項目 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf index d75837e5bdc1d..ccf395830f588 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ko.xlf @@ -37,6 +37,21 @@ 계속하시겠습니까? 계속하면 손상된 코드가 생성될 수 있습니다. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces 가져오지 않은 네임스페이스의 항목 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf index 440afab3a461e..ed95ac58ab344 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pl.xlf @@ -37,6 +37,21 @@ Czy nadal chcesz kontynuować? Może to spowodować uszkodzenie kodu. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces elementy z nieimportowanych przestrzeni nazw diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf index 8d48712bab429..b151cdcda36d6 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.pt-BR.xlf @@ -37,6 +37,21 @@ Ainda quer continuar? Isso pode produzir código desfeito. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces itens de namespaces não importados diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf index 6e6c411e8c1e4..7d8efd9dad21d 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.ru.xlf @@ -37,6 +37,21 @@ Вы действительно хотите продолжить? Это может привести к появлению нерабочего кода. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces элементы из неимпортированных пространств имен diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf index 6d9975e997f58..6dfbacbe53de2 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.tr.xlf @@ -37,6 +37,21 @@ Yine de devam etmek istiyor musunuz? Bu işlem sonucunda bozuk kod oluşturulabilir. + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces içeri aktarılmayan ad alanlarındaki öğeler diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf index ef2b1bd04325a..ef3ac57b250de 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hans.xlf @@ -37,6 +37,21 @@ 是否仍要继续? 这可能会导致代码损坏。 + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces 未导入命名空间中的项 diff --git a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf index b5898eb69f9d8..f375a1e287679 100644 --- a/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf +++ b/src/EditorFeatures/Core/xlf/EditorFeaturesResources.zh-Hant.xlf @@ -37,6 +37,21 @@ 仍要繼續嗎? 這可能產生中斷的程式碼。 + + Downloading IntelliSense index for {0} + Downloading IntelliSense index for {0} + + + + Downloading index failed + Downloading index failed + + + + Downloading index failed:{0} + Downloading index failed:{0} + + items from unimported namespaces 來自未匯入命名空間的項目 diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 0e0e271fc361f..3241f9afe26f8 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -1063,27 +1063,6 @@ 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(); - 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() { diff --git a/src/EditorFeatures/VisualBasicTest/TypeInferrer/TypeInferrerTests.vb b/src/EditorFeatures/VisualBasicTest/TypeInferrer/TypeInferrerTests.vb index 6c9227e82289e..02396552bb687 100644 --- a/src/EditorFeatures/VisualBasicTest/TypeInferrer/TypeInferrerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/TypeInferrer/TypeInferrerTests.vb @@ -24,13 +24,9 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.TypeInferrer Dim node = FindExpressionSyntaxFromSpan(root, textSpan) Dim typeInference = document.GetLanguageService(Of ITypeInferenceService)() - Dim inferredType As ITypeSymbol - - If testMode = TestMode.Position Then - inferredType = typeInference.InferType(Await document.GetSemanticModelForSpanAsync(New TextSpan(node.SpanStart, 0), CancellationToken.None), node.SpanStart, objectAsDefault:=True, cancellationToken:=CancellationToken.None) - Else - inferredType = typeInference.InferType(Await document.GetSemanticModelForSpanAsync(node.Span, CancellationToken.None), node, objectAsDefault:=True, cancellationToken:=CancellationToken.None) - End If + Dim inferredType = If(testMode = TestMode.Position, + typeInference.InferType(Await document.ReuseExistingSpeculativeModelAsync(node.SpanStart, CancellationToken.None), node.SpanStart, objectAsDefault:=True, cancellationToken:=CancellationToken.None), + typeInference.InferType(Await document.ReuseExistingSpeculativeModelAsync(node.Span, CancellationToken.None), node, objectAsDefault:=True, cancellationToken:=CancellationToken.None)) Dim typeSyntax = inferredType.GenerateTypeSyntax().NormalizeWhitespace() Assert.Equal(expectedType, typeSyntax.ToString()) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs index 425e65fd6736f..d05c01c23c907 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/AttributeNamedParameterCompletionProvider.cs @@ -85,7 +85,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var existingNamedParameters = GetExistingNamedParameters(attributeArgumentList, position); var workspace = document.Project.Solution.Workspace; - var semanticModel = await document.GetSemanticModelForNodeAsync(attributeSyntax, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(attributeSyntax, cancellationToken).ConfigureAwait(false); var nameColonItems = await GetNameColonItemsAsync(context, semanticModel, token, attributeSyntax, existingNamedParameters).ConfigureAwait(false); var nameEqualsItems = await GetNameEqualsItemsAsync(context, semanticModel, token, attributeSyntax, existingNamedParameters).ConfigureAwait(false); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs index 2ca222826667d..2fa30692229c8 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/CrefCompletionProvider.cs @@ -128,7 +128,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) return (default, null, ImmutableArray.Empty); } - var semanticModel = await document.GetSemanticModelForNodeAsync( + var semanticModel = await document.ReuseExistingSpeculativeModelAsync( parentNode, cancellationToken).ConfigureAwait(false); var symbols = GetSymbols(token, semanticModel, cancellationToken) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs index 54e97849e1596..08a482ceb172b 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.DeclarationInfo.cs @@ -46,7 +46,7 @@ internal static async Task GetDeclarationInfoAsync(Document { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken).GetPreviousTokenIfTouchingWord(position); - var semanticModel = await document.GetSemanticModelForSpanAsync(new Text.TextSpan(token.SpanStart, 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(token.SpanStart, cancellationToken).ConfigureAwait(false); var typeInferenceService = document.GetLanguageService(); if (IsTupleTypeElement(token, semanticModel, cancellationToken, out var result) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs index ba090c53a8e6a..7721a7cee7647 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/DeclarationNameCompletionProvider.cs @@ -48,7 +48,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC var position = completionContext.Position; var document = completionContext.Document; var cancellationToken = completionContext.CancellationToken; - var semanticModel = await document.GetSemanticModelForSpanAsync(new Text.TextSpan(position, 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); if (!completionContext.Options.GetOption(CompletionOptions.ShowNameSuggestions, LanguageNames.CSharp)) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs index c0febedcd066b..b4829b064b4c3 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/EnumAndCompletionListTagCompletionProvider.cs @@ -88,8 +88,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var typeInferenceService = document.GetLanguageService(); Contract.ThrowIfNull(typeInferenceService, nameof(typeInferenceService)); - var span = new TextSpan(position, 0); - var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var types = typeInferenceService.InferTypes(semanticModel, position, cancellationToken: cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs index a4000cda41d05..4d3f12a6ec223 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceMemberCompletionProvider.cs @@ -58,14 +58,13 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var position = context.Position; var cancellationToken = context.CancellationToken; - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, length: 0), cancellationToken).ConfigureAwait(false); - var syntaxTree = semanticModel.SyntaxTree; + var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService(); var semanticFacts = document.GetRequiredLanguageService(); if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken) || - semanticFacts.IsPreProcessorDirectiveContext(semanticModel, position, cancellationToken)) + syntaxFacts.IsPreProcessorDirectiveContext(syntaxTree, position, cancellationToken)) { return; } @@ -83,6 +82,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) // Bind the interface name which is to the left of the dot var name = specifierNode.Name; + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol as ITypeSymbol; if (symbol?.TypeKind != TypeKind.Interface) return; diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs index e03c288b34152..3a5370323503c 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ExplicitInterfaceTypeCompletionProvider.cs @@ -45,7 +45,7 @@ protected override (string displayText, string suffix, string insertionText) Get protected override async Task CreateContextAsync( Document document, int position, CancellationToken cancellationToken) { - var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); return CSharpSyntaxContext.CreateContext( document.Project.Solution.Workspace, semanticModel, position, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs index 51d08ff1ef995..7fdb4682fbff5 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/KeywordCompletionProvider.cs @@ -186,8 +186,7 @@ internal override bool IsInsertionTrigger(SourceText text, int characterPosition protected override async Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) { - var span = new TextSpan(position, length: 0); - var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); return CSharpSyntaxContext.CreateContext(document.Project.Solution.Workspace, semanticModel, position, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs index 996844a7e4252..56b058651c9c3 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.cs @@ -76,7 +76,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) return; } - var semanticModel = await document.GetSemanticModelForNodeAsync(argumentList, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(argumentList, cancellationToken).ConfigureAwait(false); var parameterLists = GetParameterLists(semanticModel, position, argumentList.Parent, cancellationToken); if (parameterLists == null) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs index f9951b468394c..90ca2e1895d97 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectAndWithInitializerCompletionProvider.cs @@ -80,7 +80,7 @@ protected override async Task IsExclusiveAsync(Document document, int posi return false; } - var semanticModel = await document.GetSemanticModelForNodeAsync(expression, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(expression, cancellationToken).ConfigureAwait(false); var initializedType = semanticModel.GetTypeInfo(expression, cancellationToken).Type; if (initializedType == null) { diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs index a21d0730a25c1..4e30fa439f72d 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.cs @@ -65,7 +65,7 @@ protected override SyntaxNode GetObjectCreationNewExpression(SyntaxTree tree, in protected override async Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) { - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); return CSharpSyntaxContext.CreateContext(document.Project.Solution.Workspace, semanticModel, position, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs index bc61d43cab504..334d42f43a22c 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/PropertySubPatternCompletionProvider.cs @@ -44,7 +44,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) return; } - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, length: 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var pattern = (PatternSyntax)token.Parent.Parent; var type = semanticModel.GetTypeInfo(pattern, cancellationToken).ConvertedType; if (type == null) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs index 53541c020e5ca..9e6c8da916a8e 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SnippetCompletionProvider.cs @@ -94,45 +94,46 @@ private static async Task> GetSnippetsForDocumentAsy return SpecializedCollections.EmptyEnumerable(); } - var span = new TextSpan(position, 0); - var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); var isPossibleTupleContext = syntaxFacts.IsPossibleTupleContext(syntaxTree, position, cancellationToken); - if (semanticFacts.IsPreProcessorDirectiveContext(semanticModel, position, cancellationToken)) + if (syntaxFacts.IsPreProcessorDirectiveContext(syntaxTree, position, cancellationToken)) { var directive = leftToken.GetAncestor(); - if (directive.DirectiveNameToken.IsKind( - SyntaxKind.IfKeyword, - SyntaxKind.RegionKeyword, - SyntaxKind.ElseKeyword, - SyntaxKind.ElifKeyword, - SyntaxKind.ErrorKeyword, - SyntaxKind.LineKeyword, - SyntaxKind.PragmaKeyword, - SyntaxKind.EndIfKeyword, - SyntaxKind.UndefKeyword, - SyntaxKind.EndRegionKeyword, - SyntaxKind.WarningKeyword)) + if (!directive.DirectiveNameToken.IsKind( + SyntaxKind.IfKeyword, + SyntaxKind.RegionKeyword, + SyntaxKind.ElseKeyword, + SyntaxKind.ElifKeyword, + SyntaxKind.ErrorKeyword, + SyntaxKind.LineKeyword, + SyntaxKind.PragmaKeyword, + SyntaxKind.EndIfKeyword, + SyntaxKind.UndefKeyword, + SyntaxKind.EndRegionKeyword, + SyntaxKind.WarningKeyword)) { - return SpecializedCollections.EmptyEnumerable(); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); + return await GetSnippetCompletionItemsAsync(workspace, semanticModel, isPreProcessorContext: true, + isTupleContext: isPossibleTupleContext, cancellationToken: cancellationToken).ConfigureAwait(false); } - - return await GetSnippetCompletionItemsAsync(workspace, semanticModel, isPreProcessorContext: true, - isTupleContext: isPossibleTupleContext, cancellationToken: cancellationToken).ConfigureAwait(false); } - - if (semanticFacts.IsGlobalStatementContext(semanticModel, position, cancellationToken) || - semanticFacts.IsExpressionContext(semanticModel, position, cancellationToken) || - semanticFacts.IsStatementContext(semanticModel, position, cancellationToken) || - semanticFacts.IsTypeContext(semanticModel, position, cancellationToken) || - semanticFacts.IsTypeDeclarationContext(semanticModel, position, cancellationToken) || - semanticFacts.IsNamespaceContext(semanticModel, position, cancellationToken) || - semanticFacts.IsNamespaceDeclarationNameContext(semanticModel, position, cancellationToken) || - semanticFacts.IsMemberDeclarationContext(semanticModel, position, cancellationToken) || - semanticFacts.IsLabelContext(semanticModel, position, cancellationToken)) + else { - return await GetSnippetCompletionItemsAsync(workspace, semanticModel, isPreProcessorContext: false, - isTupleContext: isPossibleTupleContext, cancellationToken: cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); + + if (semanticFacts.IsGlobalStatementContext(semanticModel, position, cancellationToken) || + semanticFacts.IsExpressionContext(semanticModel, position, cancellationToken) || + semanticFacts.IsStatementContext(semanticModel, position, cancellationToken) || + semanticFacts.IsTypeContext(semanticModel, position, cancellationToken) || + semanticFacts.IsTypeDeclarationContext(semanticModel, position, cancellationToken) || + semanticFacts.IsNamespaceContext(semanticModel, position, cancellationToken) || + semanticFacts.IsNamespaceDeclarationNameContext(semanticModel, position, cancellationToken) || + semanticFacts.IsMemberDeclarationContext(semanticModel, position, cancellationToken) || + semanticFacts.IsLabelContext(semanticModel, position, cancellationToken)) + { + return await GetSnippetCompletionItemsAsync(workspace, semanticModel, isPreProcessorContext: false, + isTupleContext: isPossibleTupleContext, cancellationToken: cancellationToken).ConfigureAwait(false); + } } return SpecializedCollections.EmptyEnumerable(); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs index 13b5c9091ca7b..12574148d22a7 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SpeculativeTCompletionProvider.cs @@ -74,7 +74,7 @@ private static async Task ShouldShowSpeculativeTCompletionItemAsync(Docume // If we managed to walk out and get a different SpanStart, we treat it as a simple $$T case. var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); - var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(token.Parent, cancellationToken).ConfigureAwait(false); var spanStart = position; while (true) diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs index a4522baebfccf..9e25084802865 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs @@ -186,8 +186,7 @@ private bool ShouldTriggerInArgumentLists(Workspace workspace, OptionSet options protected override async Task CreateContextAsync(Document document, int position, CancellationToken cancellationToken) { var workspace = document.Project.Solution.Workspace; - var span = new TextSpan(position, 0); - var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); return CSharpSyntaxContext.CreateContext(workspace, semanticModel, position, cancellationToken); } diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs index 657c312dc930d..43a3b6c59f557 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/TupleNameCompletionProvider.cs @@ -42,7 +42,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext completionC var position = completionContext.Position; var cancellationToken = completionContext.CancellationToken; - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var workspace = document.Project.Solution.Workspace; var context = CSharpSyntaxContext.CreateContext(workspace, semanticModel, position, cancellationToken); diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs index 9a35ce52b6a7e..0fee7fa0d5d88 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.cs @@ -64,7 +64,7 @@ protected override async Task> GetItemsWorkerAsync( return null; } - var semanticModel = await document.GetSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false); ISymbol declaredSymbol = null; var memberDeclaration = attachedToken.GetAncestor(); diff --git a/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs index 70c808b162ab8..d74881b971fe2 100644 --- a/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/SuggestionMode/CSharpSuggestionModeCompletionProvider.cs @@ -48,7 +48,7 @@ protected override async Task GetSuggestionModeItemAsync( return null; } - var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(token.Parent, cancellationToken).ConfigureAwait(false); var typeInferrer = document.GetLanguageService(); if (IsLambdaExpression(semanticModel, position, token, typeInferrer, cancellationToken)) { diff --git a/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs index 4c4ddae4ebddc..81c2831d91e16 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/AttributeSignatureHelpProvider.cs @@ -69,7 +69,7 @@ protected override async Task GetItemsWorkerAsync(Document d return null; } - var semanticModel = await document.GetSemanticModelForNodeAsync(attribute, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(attribute, cancellationToken).ConfigureAwait(false); if (!(semanticModel.GetTypeInfo(attribute, cancellationToken).Type is INamedTypeSymbol attributeType)) { return null; diff --git a/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs index 9768d291aee3c..6c560fdefed7d 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/GenericNameSignatureHelpProvider.cs @@ -87,7 +87,7 @@ protected override async Task GetItemsWorkerAsync(Document d var beforeDotExpression = simpleName.IsRightSideOfDot() ? simpleName.GetLeftSideOfDot() : null; - var semanticModel = await document.GetSemanticModelForNodeAsync(simpleName, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(simpleName, cancellationToken).ConfigureAwait(false); var leftSymbol = beforeDotExpression == null ? null diff --git a/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs index 771fb5d248dd6..b02285c2eef09 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.cs @@ -64,7 +64,7 @@ protected override async Task GetItemsWorkerAsync(Document d return null; } - var semanticModel = await document.GetSemanticModelForNodeAsync(invocationExpression, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(invocationExpression, cancellationToken).ConfigureAwait(false); var within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken); if (within == null) { diff --git a/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.cs b/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.cs index e939822dbc1a5..fb9d31c368417 100644 --- a/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.cs +++ b/src/Features/CSharp/Portable/SignatureHelp/ObjectCreationExpressionSignatureHelpProvider.cs @@ -58,7 +58,7 @@ protected override async Task GetItemsWorkerAsync(Document d return null; } - var semanticModel = await document.GetSemanticModelForNodeAsync(objectCreationExpression, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(objectCreationExpression, cancellationToken).ConfigureAwait(false); if (!(semanticModel.GetTypeInfo(objectCreationExpression, cancellationToken).Type is INamedTypeSymbol type)) { return null; diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractInternalsVisibleToCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractInternalsVisibleToCompletionProvider.cs index 7c04b315ceab5..5a3302590b007 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractInternalsVisibleToCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractInternalsVisibleToCompletionProvider.cs @@ -118,7 +118,7 @@ private static SyntaxNode GetAttributeSyntaxNodeOfToken(ISyntaxFactsService synt private static async Task CheckTypeInfoOfAttributeAsync(Document document, SyntaxNode attributeNode, CancellationToken cancellationToken) { - var semanticModel = await document.GetSemanticModelForNodeAsync(attributeNode, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(attributeNode, cancellationToken).ConfigureAwait(false); var typeInfo = semanticModel.GetTypeInfo(attributeNode, cancellationToken); var type = typeInfo.Type; if (type == null) @@ -232,7 +232,7 @@ private async Task GetAssemblyNameFromInternalsVisibleToAttributeAsync(D return string.Empty; } - var semanticModel = await document.GetSemanticModelForNodeAsync(constructorArgument, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(constructorArgument, cancellationToken).ConfigureAwait(false); var constantCandidate = semanticModel.GetConstantValue(constructorArgument, cancellationToken); if (constantCandidate.HasValue && constantCandidate.Value is string argument) { diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs index df121d6e4f103..f1f1a343c956b 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractObjectInitializerCompletionProvider.cs @@ -28,7 +28,7 @@ public override async Task ProvideCompletionsAsync(CompletionContext context) var cancellationToken = context.CancellationToken; var workspace = document.Project.Solution.Workspace; - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, length: 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var typeAndLocation = GetInitializedType(document, semanticModel, position, cancellationToken); if (typeAndLocation == null) diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.ItemGetter.cs b/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.ItemGetter.cs index 0db316847b7db..ed083e72d0abf 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.ItemGetter.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractOverrideCompletionProvider.ItemGetter.cs @@ -77,7 +77,7 @@ internal async Task> GetItemsAsync() return null; } - var semanticModel = await _document.GetSemanticModelForNodeAsync(startToken.Parent, _cancellationToken).ConfigureAwait(false); + var semanticModel = await _document.ReuseExistingSpeculativeModelAsync(startToken.Parent, _cancellationToken).ConfigureAwait(false); if (!_provider.TryDetermineReturnType(startToken, semanticModel, _cancellationToken, out var returnType, out var tokenAfterReturnType) || !_provider.TryDetermineModifiers(tokenAfterReturnType, _text, _startLineNumber, out var seenAccessibility, out var modifiers) || !TryDetermineOverridableMembers(semanticModel, startToken, seenAccessibility, out var overridableMembers)) diff --git a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs index 24c54b2171016..a080cc1f4d9e2 100644 --- a/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs +++ b/src/Features/Core/Portable/Completion/Providers/AbstractPartialTypeCompletionProvider.cs @@ -35,7 +35,7 @@ public sealed override async Task ProvideCompletionsAsync(CompletionContext comp if (node != null) { - var semanticModel = await document.GetSemanticModelForNodeAsync(node, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(node, cancellationToken).ConfigureAwait(false); var syntaxContext = await CreateSyntaxContextAsync(document, semanticModel, position, cancellationToken).ConfigureAwait(false); if (semanticModel.GetDeclaredSymbol(node, cancellationToken) is INamedTypeSymbol declaredSymbol) diff --git a/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs b/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs index 487285203f504..4d9cb9c4e79f2 100644 --- a/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs +++ b/src/Features/Core/Portable/SignatureHelp/AbstractSignatureHelpProvider.cs @@ -271,7 +271,7 @@ public async Task GetItemsAsync( var totalProjects = candidateLinkedProjectsAndSymbolSets.Select(c => c.Item1).Concat(document.Project.Id); - var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, 0), cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var compilation = semanticModel.Compilation; var finalItems = new List(); foreach (var item in itemsForCurrentDocument.Items) @@ -320,7 +320,7 @@ private static async Task>>> ExtractSymbolsF var syntaxTree = await related.Item1.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (!related.Item1.GetLanguageService().IsInInactiveRegion(syntaxTree, position, cancellationToken)) { - var relatedSemanticModel = await related.Item1.GetSemanticModelForSpanAsync(new TextSpan(position, 0), cancellationToken).ConfigureAwait(false); + var relatedSemanticModel = await related.Item1.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var symbolSet = related.Item2.Select(s => ((SymbolKeySignatureHelpItem)s).SymbolKey?.Resolve(relatedSemanticModel.Compilation, cancellationToken: cancellationToken).Symbol) .WhereNotNull() .ToSet(SymbolEquivalenceComparer.IgnoreAssembliesInstance); diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb index 9602b14fd4e1f..21eb844a8c4b8 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/CrefCompletionProvider.vb @@ -65,7 +65,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return End If - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(parentNode, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(parentNode, cancellationToken).ConfigureAwait(False) Dim symbols = GetSymbols(token, semanticModel, cancellationToken) If Not symbols.Any() Then @@ -103,7 +103,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return Nothing End If - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(parentNode, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(parentNode, cancellationToken).ConfigureAwait(False) Dim workspace = document.Project.Solution.Workspace Dim symbols = GetSymbols(token, semanticModel, cancellationToken) diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb index d660c772dce02..062122e03d445 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/EnumCompletionProvider.vb @@ -132,7 +132,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(New TextSpan(position, 0), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb index c9f0d0752ec43..b7a7d418bb914 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/HandlesClauseCompletionProvider.vb @@ -143,7 +143,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(New TextSpan(position, 0), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImplementsClauseCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImplementsClauseCompletionProvider.vb index a856f25bb5aac..ad4ef7d76a3f1 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImplementsClauseCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ImplementsClauseCompletionProvider.vb @@ -278,7 +278,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Private Const InsertionTextOnOpenParen As String = NameOf(InsertionTextOnOpenParen) Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(New TextSpan(position, 0), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/KeywordCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/KeywordCompletionProvider.vb index d3157fab6ca89..ef1cc99aee0bc 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/KeywordCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/KeywordCompletionProvider.vb @@ -26,8 +26,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Sub Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of VisualBasicSyntaxContext) - Dim span = New TextSpan(position, length:=0) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb index 4532d0b607d4f..c76f2999b3d87 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/NamedParameterCompletionProvider.vb @@ -66,7 +66,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End If End If - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(argumentList, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(argumentList, cancellationToken).ConfigureAwait(False) Dim parameterLists = GetParameterLists(semanticModel, position, argumentList.Parent, cancellationToken) If parameterLists Is Nothing Then Return diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb index 39ca1693db0f8..6cc4bc3193128 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/ObjectCreationCompletionProvider.vb @@ -52,7 +52,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(New TextSpan(position, 0), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/SymbolCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/SymbolCompletionProvider.vb index e0e90dde2a159..00b7473a73847 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/SymbolCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/SymbolCompletionProvider.vb @@ -79,7 +79,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers End Function Protected Overrides Async Function CreateContextAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxContext) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(New TextSpan(position, 0), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Return Await VisualBasicSyntaxContext.CreateContextAsync(document.Project.Solution.Workspace, semanticModel, position, cancellationToken).ConfigureAwait(False) End Function diff --git a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb index 299bfe75d5e1c..761bb906cee8d 100644 --- a/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/CompletionProviders/XmlDocCommentCompletionProvider.vb @@ -95,7 +95,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.Providers Return GetCloseTagItem(token.GetPreviousToken()) End If - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(False) Dim symbol As ISymbol = Nothing If declaration IsNot Nothing Then diff --git a/src/Features/VisualBasic/Portable/Completion/SuggestionMode/VisualBasicSuggestionModeCompletionProvider.vb b/src/Features/VisualBasic/Portable/Completion/SuggestionMode/VisualBasicSuggestionModeCompletionProvider.vb index f329163da89bc..0643813d504e2 100644 --- a/src/Features/VisualBasic/Portable/Completion/SuggestionMode/VisualBasicSuggestionModeCompletionProvider.vb +++ b/src/Features/VisualBasic/Portable/Completion/SuggestionMode/VisualBasicSuggestionModeCompletionProvider.vb @@ -29,8 +29,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Completion.SuggestionMode Protected Overrides Async Function GetSuggestionModeItemAsync(document As Document, position As Integer, itemSpan As TextSpan, trigger As CompletionTrigger, cancellationToken As CancellationToken) As Task(Of CompletionItem) Dim text = Await document.GetTextAsync(cancellationToken).ConfigureAwait(False) - Dim span = New TextSpan(position, 0) - Dim semanticModel = Await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(False) Dim syntaxTree = semanticModel.SyntaxTree ' If we're option explicit off, then basically any expression context can have a diff --git a/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb b/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb index 02aca0ff9e616..6cd6404c81493 100644 --- a/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb +++ b/src/Features/VisualBasic/Portable/SignatureHelp/AbstractIntrinsicOperatorSignatureHelpProvider.vb @@ -38,7 +38,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Dim items As New List(Of SignatureHelpItem) - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(node, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(node, cancellationToken).ConfigureAwait(False) For Each documentation In Await GetIntrinsicOperatorDocumentationAsync(node, document, cancellationToken).ConfigureAwait(False) Dim signatureHelpItem = GetSignatureHelpItemForIntrinsicOperator(document, semanticModel, node.SpanStart, documentation, cancellationToken) items.Add(signatureHelpItem) diff --git a/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb b/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb index bd4d71974d97f..452cbeb4e4343 100644 --- a/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb +++ b/src/Features/VisualBasic/Portable/SignatureHelp/GenericNameSignatureHelpProvider.vb @@ -69,7 +69,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp End If Dim beforeDotExpression = If(genericName.IsRightSideOfDotOrBang(), genericName.GetLeftSideOfDot(), Nothing) - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(If(beforeDotExpression, genericName), cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(If(beforeDotExpression, genericName), cancellationToken).ConfigureAwait(False) Dim leftSymbol = If(beforeDotExpression Is Nothing, Nothing, TryCast(semanticModel.GetSymbolInfo(beforeDotExpression, cancellationToken).GetAnySymbol(), INamespaceOrTypeSymbol)) diff --git a/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb b/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb index 54695be2d5248..85711855ee1b3 100644 --- a/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb +++ b/src/Features/VisualBasic/Portable/SignatureHelp/InvocationExpressionSignatureHelpProvider.vb @@ -70,7 +70,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.SignatureHelp Return Nothing End If - Dim semanticModel = Await document.GetSemanticModelForNodeAsync(invocationExpression, cancellationToken).ConfigureAwait(False) + Dim semanticModel = Await document.ReuseExistingSpeculativeModelAsync(invocationExpression, cancellationToken).ConfigureAwait(False) Dim within = semanticModel.GetEnclosingNamedTypeOrAssembly(position, cancellationToken) If within Is Nothing Then Return Nothing diff --git a/src/Tools/BuildBoss/ProjectCheckerUtil.cs b/src/Tools/BuildBoss/ProjectCheckerUtil.cs index f8218a6ba4926..30b8b9636541b 100644 --- a/src/Tools/BuildBoss/ProjectCheckerUtil.cs +++ b/src/Tools/BuildBoss/ProjectCheckerUtil.cs @@ -101,7 +101,6 @@ private bool CheckProjectReferences(TextWriter textWriter) var declaredList = declaredEntryList.Select(x => x.ProjectKey).ToList(); allGood &= CheckProjectReferencesComplete(textWriter, declaredList); allGood &= CheckUnitTestReferenceRestriction(textWriter, declaredList); - allGood &= CheckTransitiveReferences(textWriter, declaredList); allGood &= CheckNoGuidsOnProjectReferences(textWriter, declaredEntryList); return allGood; @@ -260,63 +259,6 @@ private bool CheckUnitTestReferenceRestriction(TextWriter textWriter, IEnumerabl return allGood; } - /// - /// In order to ensure all dependencies are properly copied on deployment projects, the declared reference - /// set much match the transitive dependency set. When there is a difference it represents dependencies that - /// MSBuild won't deploy on build. - /// - private bool CheckTransitiveReferences(TextWriter textWriter, IEnumerable declaredReferences) - { - if (!_projectUtil.IsDeploymentProject) - { - return true; - } - - var list = GetProjectReferencesTransitive(declaredReferences); - var set = new HashSet(declaredReferences); - var allGood = true; - foreach (var key in list) - { - if (!set.Contains(key)) - { - textWriter.WriteLine($"Missing project reference {key.FileName}"); - allGood = false; - } - } - - return allGood; - } - - private List GetProjectReferencesTransitive(IEnumerable declaredReferences) - { - var list = new List(); - var toVisit = new Queue(declaredReferences); - var seen = new HashSet(); - - while (toVisit.Count > 0) - { - var current = toVisit.Dequeue(); - if (!seen.Add(current)) - { - continue; - } - - if (!_solutionMap.TryGetValue(current, out var data)) - { - continue; - } - - list.Add(current); - foreach (var dep in data.ProjectUtil.GetDeclaredProjectReferences()) - { - toVisit.Enqueue(dep.ProjectKey); - } - } - - list.Sort((x, y) => x.FileName.CompareTo(y.FileName)); - return list; - } - private bool CheckTargetFrameworks(TextWriter textWriter) { if (!_data.IsUnitTestProject) diff --git a/src/Tools/BuildBoss/ProjectUtil.cs b/src/Tools/BuildBoss/ProjectUtil.cs index 25fe79d9129b2..ec6b03cd09b49 100644 --- a/src/Tools/BuildBoss/ProjectUtil.cs +++ b/src/Tools/BuildBoss/ProjectUtil.cs @@ -44,8 +44,6 @@ internal ProjectUtil(ProjectKey key, XDocument document) OutputType = FindSingleProperty("OutputType")?.Value.Trim().ToLowerInvariant(); } - internal bool IsDeploymentProject => IsTestProject || OutputType switch { "exe" => true, "winexe" => true, _ => false }; - internal XElement GetTargetFramework() => Document.XPathSelectElements("//mb:TargetFramework", Manager).FirstOrDefault(); internal XElement GetTargetFrameworks() => Document.XPathSelectElements("//mb:TargetFrameworks", Manager).FirstOrDefault(); diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb index 39ceba32daf37..4a63182bbc1c6 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/GreenNodes/GreenNodeWriter.vb @@ -848,15 +848,15 @@ Friend Class GreenNodeWriter ' visit all children For i = 0 To allChildren.Count - 1 If allChildren(i).IsList Then - _writer.WriteLine(" Dim {0} = VisitList(node.{1})" + vbCrLf + + _writer.WriteLine(" Dim {0} = VisitList(node.{1})" + Environment.NewLine + " If node.{2} IsNot {0}.Node Then anyChanges = True", ChildNewVarName(allChildren(i)), ChildPropertyName(allChildren(i)), ChildVarName(allChildren(i))) ElseIf KindTypeStructure(allChildren(i).ChildKind).IsToken Then - _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + vbCrLf + + _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + Environment.NewLine + " If node.{3} IsNot {0} Then anyChanges = True", ChildNewVarName(allChildren(i)), BaseTypeReference(allChildren(i)), ChildPropertyName(allChildren(i)), ChildVarName(allChildren(i))) Else - _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + vbCrLf + + _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + Environment.NewLine + " If node.{2} IsNot {0} Then anyChanges = True", ChildNewVarName(allChildren(i)), ChildPropertyTypeRef(nodeStructure, allChildren(i)), ChildVarName(allChildren(i))) End If diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/RedNodeWriter.vb b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/RedNodeWriter.vb index b75838d5af889..7b2a94bbc7872 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/RedNodeWriter.vb +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/RedNodeWriter.vb @@ -168,7 +168,7 @@ Friend Class RedNodeWriter _writer.Write(" Case SyntaxKind." & kind.Name) first = False Else - _writer.Write("," & vbCrLf) + _writer.Write("," & Environment.NewLine) _writer.Write(" SyntaxKind." & kind.Name) End If @@ -1170,11 +1170,11 @@ Friend Class RedNodeWriter End If ElseIf KindTypeStructure(child.ChildKind).IsToken Then - _writer.WriteLine(" Dim {0} = DirectCast(VisitToken(node.{2}).Node, {3})" + vbCrLf + + _writer.WriteLine(" Dim {0} = DirectCast(VisitToken(node.{2}).Node, {3})" + Environment.NewLine + " If node.{2}.Node IsNot {0} Then anyChanges = True", ChildNewVarName(child), BaseTypeReference(child), ChildPropertyName(child), ChildConstructorTypeRef(child)) Else - _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + vbCrLf + + _writer.WriteLine(" Dim {0} = DirectCast(Visit(node.{2}), {1})" + Environment.NewLine + " If node.{2} IsNot {0} Then anyChanges = True", ChildNewVarName(child), ChildPropertyTypeRef(nodeStructure, child), ChildPropertyName(child)) End If diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/SyntaxFactsWriter.vb b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/SyntaxFactsWriter.vb index 241187fdcb0cd..0344d9a72b153 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/SyntaxFactsWriter.vb +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/RedNodes/SyntaxFactsWriter.vb @@ -86,6 +86,10 @@ Public Class SyntaxFactsWriter tokenText = tokenText.Replace("""", """""") End If + If tokenText IsNot Nothing AndAlso tokenText.Contains(vbCrLf) Then + tokenText = tokenText.Replace(vbCrLf, Environment.NewLine) + End If + If tokenText <> Nothing Then _writer.WriteLine(" Case SyntaxKind.{0}", kind.Name) diff --git a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs index 5bf77929b7f3f..f3a3bc1d18da8 100644 --- a/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs +++ b/src/VisualStudio/CSharp/Impl/LanguageService/CSharpHelpContextService.cs @@ -61,7 +61,7 @@ public override async Task GetHelpTermAsync(Document document, TextSpan if (IsValid(token, span)) { - var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.ReuseExistingSpeculativeModelAsync(span, cancellationToken).ConfigureAwait(false); var result = TryGetText(token, semanticModel, document, syntaxFacts, cancellationToken); if (string.IsNullOrEmpty(result)) diff --git a/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs new file mode 100644 index 0000000000000..f814c297d6514 --- /dev/null +++ b/src/Workspaces/CSharp/Portable/SemanticModelReuse/CSharpSemanticModelReuseLanguageService.cs @@ -0,0 +1,82 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Composition; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp.LanguageServices; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.LanguageServices; +using Microsoft.CodeAnalysis.SemanticModelReuse; + +namespace Microsoft.CodeAnalysis.CSharp.SemanticModelReuse +{ + [ExportLanguageService(typeof(ISemanticModelReuseLanguageService), LanguageNames.CSharp), Shared] + internal class CSharpSemanticModelReuseLanguageService : AbstractSemanticModelReuseLanguageService< + BaseMethodDeclarationSyntax, + AccessorDeclarationSyntax, + PropertyDeclarationSyntax, + EventDeclarationSyntax> + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public CSharpSemanticModelReuseLanguageService() + { + } + + protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance; + + protected override SyntaxList GetAccessors(EventDeclarationSyntax @event) + => @event.AccessorList?.Accessors ?? default; + + protected override SyntaxList GetAccessors(PropertyDeclarationSyntax property) + => property.AccessorList?.Accessors ?? default; + + public override SyntaxNode? TryGetContainingMethodBodyForSpeculation(SyntaxNode node) + { + for (SyntaxNode? previous = null, current = node; current != null; previous = current, current = current.Parent) + { + // These are the exact types that SemanticModel.TryGetSpeculativeSemanticModelForMethodBody accepts. + if (current is BaseMethodDeclarationSyntax baseMethod) + return previous != null && baseMethod.Body == previous ? baseMethod : null; + + if (current is AccessorDeclarationSyntax accessor) + return previous != null && accessor.Body == previous ? accessor : null; + } + + return null; + } + + protected override async Task TryGetSpeculativeSemanticModelWorkerAsync( + SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken) + { + 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; + } + } +} diff --git a/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs b/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs index f2794b0cf60e5..882b03db6755c 100644 --- a/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs +++ b/src/Workspaces/Core/Portable/CaseCorrection/AbstractCaseCorrectionService.cs @@ -34,7 +34,7 @@ public async Task CaseCorrectAsync(Document document, ImmutableArray> GetClassifiedSpansAsync( TextSpan textSpan, CancellationToken cancellationToken = default) { - var semanticModel = await document.GetSemanticModelForSpanAsync(textSpan, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); return GetClassifiedSpans(semanticModel, textSpan, document.Project.Solution.Workspace, cancellationToken); } diff --git a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs index 1270266f80f1b..c8e92e9075903 100644 --- a/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs +++ b/src/Workspaces/Core/Portable/Classification/SyntaxClassification/AbstractSyntaxClassificationService.cs @@ -37,7 +37,7 @@ public async Task AddSemanticClassificationsAsync( { try { - var semanticModel = await document.GetSemanticModelForSpanAsync(textSpan, cancellationToken).ConfigureAwait(false); + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); AddSemanticClassifications(semanticModel, textSpan, document.Project.Solution.Workspace, getNodeClassifiers, getTokenClassifiers, result, cancellationToken); } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) diff --git a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaDocumentExtensions.cs b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaDocumentExtensions.cs index 96bf12f3e016e..b02c7ca2ad297 100644 --- a/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaDocumentExtensions.cs +++ b/src/Workspaces/Core/Portable/ExternalAccess/Pythia/Api/PythiaDocumentExtensions.cs @@ -13,6 +13,6 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.Pythia.Api internal static class PythiaDocumentExtensions { public static Task GetSemanticModelForNodeAsync(this Document document, SyntaxNode? node, CancellationToken cancellationToken) - => DocumentExtensions.GetSemanticModelForNodeAsync(document, node, cancellationToken); + => DocumentExtensions.ReuseExistingSpeculativeModelAsync(document, node, cancellationToken); } } diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs new file mode 100644 index 0000000000000..ab82d0aff0185 --- /dev/null +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/AbstractSemanticModelReuseLanguageService.cs @@ -0,0 +1,96 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Diagnostics; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.LanguageServices; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SemanticModelReuse +{ + internal abstract class AbstractSemanticModelReuseLanguageService< + TBaseMethodDeclarationSyntax, + TAccessorDeclarationSyntax, + TPropertyDeclarationSyntax, + TEventDeclarationSyntax> : ISemanticModelReuseLanguageService + where TBaseMethodDeclarationSyntax : SyntaxNode + where TAccessorDeclarationSyntax : SyntaxNode + where TPropertyDeclarationSyntax : SyntaxNode + where TEventDeclarationSyntax : SyntaxNode + { + protected abstract ISyntaxFacts SyntaxFacts { get; } + + public abstract SyntaxNode? TryGetContainingMethodBodyForSpeculation(SyntaxNode node); + + protected abstract Task TryGetSpeculativeSemanticModelWorkerAsync(SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken); + protected abstract SyntaxList GetAccessors(TPropertyDeclarationSyntax property); + protected abstract SyntaxList GetAccessors(TEventDeclarationSyntax @event); + + public Task TryGetSpeculativeSemanticModelAsync(SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken) + { + var previousSyntaxTree = previousSemanticModel.SyntaxTree; + var currentSyntaxTree = currentBodyNode.SyntaxTree; + + // This operation is only valid if top-level equivalent trees were passed in. + Contract.ThrowIfFalse(previousSyntaxTree.IsEquivalentTo(currentSyntaxTree, topLevel: true)); + return TryGetSpeculativeSemanticModelWorkerAsync(previousSemanticModel, currentBodyNode, cancellationToken); + } + + protected SyntaxNode GetPreviousBodyNode(SyntaxNode previousRoot, SyntaxNode currentRoot, SyntaxNode currentBodyNode) + { + if (currentBodyNode is TAccessorDeclarationSyntax currentAccessor) + { + // in the case of an accessor, have to find the previous accessor in the previous prop/event corresponding + // to the current prop/event. + + var currentContainer = currentBodyNode.Ancestors().First(a => a is TEventDeclarationSyntax || a is TPropertyDeclarationSyntax); + var previousContainer = GetPreviousBodyNode(previousRoot, currentRoot, currentContainer); + + var currentAccessors = GetAccessors(currentContainer); + var previousAccessors = GetAccessors(previousContainer); + + if (currentAccessors.Count != previousAccessors.Count) + { + Debug.Fail("Accessor count shouldn't have changed as there were no top level edits."); + return null; + } + + return previousAccessors[currentAccessors.IndexOf(currentAccessor)]; + } + else + { + var currentMembers = this.SyntaxFacts.GetMethodLevelMembers(currentRoot); + var index = currentMembers.IndexOf(currentBodyNode); + if (index < 0) + { + Debug.Fail($"Unhandled member type in {nameof(GetPreviousBodyNode)}"); + return null; + } + + var previousMembers = this.SyntaxFacts.GetMethodLevelMembers(previousRoot); + if (currentMembers.Count != previousMembers.Count) + { + Debug.Fail("Member count shouldn't have changed as there were no top level edits."); + return null; + } + + return previousMembers[index]; + } + } + + private SyntaxList GetAccessors(SyntaxNode container) + { + return container switch + { + TPropertyDeclarationSyntax currentProperty => GetAccessors(currentProperty), + TEventDeclarationSyntax currentEvent => GetAccessors(currentEvent), + _ => throw ExceptionUtilities.Unreachable, + }; + } + } +} diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/ISemanticModelReuseLanguageService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/ISemanticModelReuseLanguageService.cs new file mode 100644 index 0000000000000..0b42ad524cbae --- /dev/null +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/ISemanticModelReuseLanguageService.cs @@ -0,0 +1,42 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.LanguageServices; + +namespace Microsoft.CodeAnalysis.SemanticModelReuse +{ + /// + /// Interface only for use by . Includes language specific + /// implementations on how to get an appropriate speculated semantic model given an older semantic model and a + /// changed method body. + /// + internal interface ISemanticModelReuseLanguageService : ILanguageService + { + /// + /// Given a node, returns the parent method-body-esque node that we can get a new speculative semantic model + /// for. Returns if not in such a location. + /// + SyntaxNode? TryGetContainingMethodBodyForSpeculation(SyntaxNode node); + + /// + /// Given a previous semantic model, and a method-eque node in the current tree for that same document, attempts + /// to create a new speculative semantic model using the top level symbols of but the new body level symbols produced for . + /// + /// Note: it is critical that no top level changes have occurred between the syntax tree that points at and the syntax tree that points + /// at. In other words, they must be (..., topLevel: true). This + /// function is undefined if they are not. + /// + /// + Task TryGetSpeculativeSemanticModelAsync(SemanticModel previousSemanticModel, SyntaxNode currentBodyNode, CancellationToken cancellationToken); + } +} diff --git a/src/Workspaces/Core/Portable/SemanticModelReuse/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs b/src/Workspaces/Core/Portable/SemanticModelReuse/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs new file mode 100644 index 0000000000000..8fab93537e993 --- /dev/null +++ b/src/Workspaces/Core/Portable/SemanticModelReuse/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs @@ -0,0 +1,211 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System.Collections.Immutable; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; +using Microsoft.CodeAnalysis.PooledObjects; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.SemanticModelReuse +{ + internal readonly struct SemanticModelReuseInfo + { + /// + /// The original non-speculative semantic model we retrieved for this document at some point. + /// + public readonly SemanticModel PreviousNonSpeculativeSemanticModel; + + /// + /// The current semantic model we retrieved for the . Could + /// be speculative or non-speculative. + /// + public readonly SemanticModel CurrentSemanticModel; + + /// + /// The current method body we retrieved the for. + /// + public readonly SyntaxNode BodyNode; + + /// + /// The top level version of the project when we retrieved . As long as this is the + /// same we can continue getting speculative models to use. + /// + public readonly VersionStamp TopLevelSementicVersion; + + public SemanticModelReuseInfo(SemanticModel previousNonSpeculativeSemanticModel, SemanticModel currentSemanticModel, SyntaxNode bodyNode, VersionStamp topLevelSementicVersion) + { + PreviousNonSpeculativeSemanticModel = previousNonSpeculativeSemanticModel; + CurrentSemanticModel = currentSemanticModel; + BodyNode = bodyNode; + TopLevelSementicVersion = topLevelSementicVersion; + } + } + + internal partial class SemanticModelReuseWorkspaceServiceFactory : IWorkspaceServiceFactory + { + private sealed class SemanticModelReuseWorkspaceService : ISemanticModelReuseWorkspaceService + { + private readonly Workspace _workspace; + + /// + /// A mapping from a document id to the last semantic model we produced for it, along with the top level + /// semantic version that that semantic model corresponds to. We can continue reusing the semantic model as + /// long as no top level changes occur. + /// + /// In general this dictionary will only contain a single key-value pair. However, in the case of linked + /// documents, there will be a key-value pair for each of the independent document links that a document + /// has. + /// + /// + /// A value simply means we haven't cached any information for that particular id. + /// + /// + private ImmutableDictionary _semanticModelMap = ImmutableDictionary.Empty; + + public SemanticModelReuseWorkspaceService(Workspace workspace) + { + _workspace = workspace; + _workspace.WorkspaceChanged += (_, e) => + { + // if our map points at documents not in the current solution, then we want to clear things out. + // this way we don't hold onto semantic models past, say, the c#/vb solutions closing. + var map = _semanticModelMap; + if (map.IsEmpty) + return; + + var solution = e.NewSolution; + foreach (var (docId, _) in map) + { + if (!solution.ContainsDocument(docId)) + { + _semanticModelMap = ImmutableDictionary.Empty; + return; + } + } + }; + } + + public async Task ReuseExistingSpeculativeModelAsync(Document document, SyntaxNode node, CancellationToken cancellationToken) + { + var reuseService = document.GetRequiredLanguageService(); + + // 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. + var bodyNode = reuseService.TryGetContainingMethodBodyForSpeculation(node); + if (bodyNode == null) + return await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + // We were in a method body. Compute the updated map that will contain the appropriate semantic model + // for this document. + // + // In terms of concurrency we the map so that we can operate on it independently of other threads. When + // we compute the final map, we'll grab the semantic model out of it to return (which must be correct + // since we're the thread that created that map). Then, we overwrite the instance map with our final + // map. This map may be stomped on by another thread, but that's fine. We don't have any sort of + // ordering requirement. We just want someone to win and place the new map so that it's there for the + // next caller (which is likely to use the same body node). + var originalMap = _semanticModelMap; + + // If we already have a cached *real* semantic model for this body, then just provide that. Note: this + // is also a requirement as you cannot speculate on a semantic model using a node from that same + // semantic model. + if (originalMap.TryGetValue(document.Id, out var reuseInfoOpt) && + reuseInfoOpt.HasValue && + reuseInfoOpt.Value.PreviousNonSpeculativeSemanticModel.SyntaxTree == bodyNode.SyntaxTree) + { + return reuseInfoOpt.Value.PreviousNonSpeculativeSemanticModel; + } + + var updatedMap = await ComputeUpdatedMapAsync(originalMap, document, bodyNode, cancellationToken).ConfigureAwait(false); + + // Grab the resultant semantic model and then overwrite the existing map. We return the semantic model + // from the map *we* computed so that we're isolated from other threads writing to the map stored in the + // field. + var info = updatedMap[document.Id]!.Value; + var semanticModel = info.CurrentSemanticModel; + Interlocked.CompareExchange(ref _semanticModelMap, updatedMap, originalMap); + + return semanticModel; + } + + private static async Task> ComputeUpdatedMapAsync( + ImmutableDictionary map, Document document, SyntaxNode bodyNode, CancellationToken cancellationToken) + { + var linkedIds = document.GetLinkedDocumentIds(); + + // Get the current top level version for this document's project. If it has changed, then we cannot + // reuse any existing cached data for it. This also ensures that we can do things like find the same + // method body node prior to an edit just by counting it's top-level index in the file. + var topLevelSemanticVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + // If we are able to reuse a semantic model, then ensure that this is now the semantic model we're now + // pointing at for this document. + var reuseInfo = await TryReuseCachedSemanticModelAsync( + map, document, bodyNode, topLevelSemanticVersion, cancellationToken).ConfigureAwait(false); + if (reuseInfo != null) + return map.SetItem(document.Id, reuseInfo.Value); + + // Otherwise, we couldn't reuse that doc's cached info. Create a fresh map with that doc's real + // semantic model value in it. Note: we still reuse the values stored with the other links for that + // doc as they may still be valid to use. + var semanticModel = await document.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false); + + var builder = ImmutableDictionary.CreateBuilder(); + + // Note: we are intentionally storing the semantic model instance in the speculative location as well. + builder.Add(document.Id, new SemanticModelReuseInfo(semanticModel, semanticModel, bodyNode, topLevelSemanticVersion)); + + foreach (var linkedId in linkedIds) + { + // Reuse the existing cached data for any links we have as well + var linkedReuseInfo = map.TryGetValue(linkedId, out var info) ? info : null; + builder.Add(linkedId, linkedReuseInfo); + } + + return builder.ToImmutable(); + } + + private static async Task TryReuseCachedSemanticModelAsync( + ImmutableDictionary map, + Document document, + SyntaxNode bodyNode, + VersionStamp topLevelSemanticVersion, + CancellationToken cancellationToken) + { + // if this is asking about a doc we don't know about, we can't reuse anything. + if (!map.ContainsKey(document.Id)) + return null; + + // see if this doc matches the docs we're caching information for. + if (!map.TryGetValue(document.Id, out var reuseInfoOpt) || !reuseInfoOpt.HasValue) + return null; + + var reuseInfo = reuseInfoOpt.Value; + + // can only reuse the cache if nothing top level changed. + if (reuseInfo.TopLevelSementicVersion != topLevelSemanticVersion) + return null; + + // If multiple callers are asking for the exact same body, they can share the exact same semantic model. + // This is valuable when several clients (like completion providers) get called at the same time on the + // same method body edit. + if (reuseInfo.BodyNode == bodyNode) + return reuseInfo; + + var reuseService = document.GetRequiredLanguageService(); + var semanticModel = await reuseService.TryGetSpeculativeSemanticModelAsync(reuseInfo.PreviousNonSpeculativeSemanticModel, bodyNode, cancellationToken).ConfigureAwait(false); + if (semanticModel == null) + return null; + + return new SemanticModelReuseInfo(reuseInfo.PreviousNonSpeculativeSemanticModel, semanticModel, bodyNode, topLevelSemanticVersion); + } + } + } +} diff --git a/src/Workspaces/Core/Portable/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs b/src/Workspaces/Core/Portable/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs deleted file mode 100644 index d4655e92ee9e2..0000000000000 --- a/src/Workspaces/Core/Portable/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.SemanticModelWorkspaceService.cs +++ /dev/null @@ -1,512 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#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.Shared.Extensions; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.SemanticModelWorkspaceService -{ - internal partial class SemanticModelWorkspaceServiceFactory : IWorkspaceServiceFactory - { - private sealed class SemanticModelService : ISemanticModelService - { - private static readonly ConditionalWeakTable>> s_map = - new ConditionalWeakTable>>(); - - private static readonly ConditionalWeakTable>> s_semanticModelMap = - new ConditionalWeakTable>>(); - - private readonly ReaderWriterLockSlim _gate = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); - - public async Task GetSemanticModelForNodeAsync(Document document, SyntaxNode node, CancellationToken cancellationToken = default) - { - var syntaxFactsService = document.GetLanguageService(); - var semanticFactsService = document.GetLanguageService(); - - if (syntaxFactsService == null || semanticFactsService == null) - { - // it only works if we can track member - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - if (IsPrimaryBranch(document) && !document.IsOpen()) - { - // for ones in primary branch, we only support opened documents (mostly to help typing scenario) - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - var versionMap = GetVersionMapFromBranchOrPrimary(document.Project.Solution.Workspace, document.Project.Solution.BranchId); - - var projectId = document.Project.Id; - var version = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - CompilationSet? compilationSet; - using (_gate.DisposableRead()) - { - versionMap.TryGetValue(projectId, out compilationSet); - } - - // this is first time - if (compilationSet == null) - { - // update the cache - await AddVersionCacheAsync(document.Project, version, cancellationToken).ConfigureAwait(false); - - // get the base one - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - // we have compilation set check whether it is something we can use - if (version.Equals(compilationSet.Version)) - { - if (!compilationSet.Compilation.TryGetValue(out var oldCompilationOpt) || !oldCompilationOpt.HasValue) - { - await AddVersionCacheAsync(document.Project, version, cancellationToken).ConfigureAwait(false); - - // get the base one - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - var oldCompilation = oldCompilationOpt.Value; - - // first check whether the set has this document - if (!compilationSet.Trees.TryGetValue(document.Id, out var oldTree)) - { - // noop. - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - // Yes, we have compilation we might be able to re-use - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(root, "We should support syntax trees if we're this far."); - if (root.SyntaxTree == oldTree) - { - // the one we have and the one in the document is same one. but tree in other file might - // have changed (no top level change). in that case, just use one from the document. - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - // let's track member that we can re-use - var member = syntaxFactsService.GetContainingMemberDeclaration(root, node.SpanStart); - if (!syntaxFactsService.IsMethodLevelMember(member)) - { - // oops, given node is not something we can support - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - // check whether we already have speculative semantic model for this - var cachedModel = GetCachedSemanticModel(oldCompilation, member); - if (cachedModel != null) - { - // Yes! - return cachedModel; - } - - // alright, we have member id. find same member from old compilation - var memberId = syntaxFactsService.GetMethodLevelMemberId(root, member); - var oldRoot = await oldTree.GetRootAsync(cancellationToken).ConfigureAwait(false); - - var oldMember = syntaxFactsService.GetMethodLevelMember(oldRoot, memberId); - if (oldMember == null) - { - // oops, something went wrong. we can't find old member. - // - // due to how we do versioning (filestamp based versioning), there is always a possibility that - // sources get changed without proper version changes in some rare situations, - // so in those rare cases which we can't control until we move to content based versioning, - // just bail out and use full semantic model - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - var oldModel = oldCompilation.GetSemanticModel(oldTree); - if (!semanticFactsService.TryGetSpeculativeSemanticModel(oldModel, oldMember, member, out var model)) - { - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - // cache the new speculative semantic model for the given node - Contract.ThrowIfNull(model); - return CacheSemanticModel(oldCompilation, member, model); - } - - // oops, it looks like we can't use cached one. - // update the cache - await UpdateVersionCacheAsync(document.Project, version, compilationSet, cancellationToken).ConfigureAwait(false); - - // get the base one - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - private static bool IsPrimaryBranch(Document document) - => document.Project.Solution.BranchId == document.Project.Solution.Workspace.PrimaryBranchId; - - private Task AddVersionCacheAsync(Project project, VersionStamp version, CancellationToken cancellationToken) - => UpdateVersionCacheAsync(project, version, primarySet: null, cancellationToken: cancellationToken); - - private async Task UpdateVersionCacheAsync(Project project, VersionStamp version, CompilationSet? primarySet, CancellationToken cancellationToken) - { - var versionMap = GetVersionMapFromBranch(project.Solution.Workspace, project.Solution.BranchId); - if (!AlreadyHasLatestCompilationSet(versionMap, project.Id, version, out var compilationSet) || - !compilationSet.Compilation.TryGetValue(out var compilationOpt) || - !compilationOpt.HasValue) - { - var newSet = await CompilationSet.CreateAsync(project, compilationSet ?? primarySet, cancellationToken).ConfigureAwait(false); - - using (_gate.DisposableWrite()) - { - // we still don't have it or if someone has beaten us, check what we have is newer - if (!versionMap.TryGetValue(project.Id, out compilationSet) || version != compilationSet.Version) - { - versionMap[project.Id] = newSet; - } - } - } - } - - private bool AlreadyHasLatestCompilationSet( - Dictionary versionMap, ProjectId projectId, VersionStamp version, [NotNullWhen(true)] out CompilationSet? compilationSet) - { - using (_gate.DisposableRead()) - { - // we still don't have it or if someone has beaten us, check what we have is newer - return versionMap.TryGetValue(projectId, out compilationSet) && version == compilationSet.Version; - } - } - - private static readonly ConditionalWeakTable>.CreateValueCallback s_createVersionMap = - _ => new Dictionary(); - - private static readonly ConditionalWeakTable>>.CreateValueCallback s_createNodeMap = - _ => new ConditionalWeakTable>(); - - private static SemanticModel? GetCachedSemanticModel(ConditionalWeakTable> nodeMap, SyntaxNode newMember) - => nodeMap.TryGetValue(newMember, out var cached) && cached.TryGetTarget(out var model) ? model : null; - - private static SemanticModel? GetCachedSemanticModel(Compilation oldCompilation, SyntaxNode newMember) - { - var nodeMap = s_semanticModelMap.GetValue(oldCompilation, s_createNodeMap); - - // see whether we have cached one - return GetCachedSemanticModel(nodeMap, newMember); - } - - private static SemanticModel CacheSemanticModel(Compilation oldCompilation, SyntaxNode newMember, SemanticModel speculativeSemanticModel) - { - var nodeMap = s_semanticModelMap.GetValue(oldCompilation, s_createNodeMap); - - // check whether somebody already have put one for me - var model = GetCachedSemanticModel(nodeMap, newMember); - if (model != null) - { - return model; - } - - // noop. put one - var weakReference = new WeakReference(speculativeSemanticModel); - var cached = nodeMap.GetValue(newMember, _ => weakReference); - if (cached.TryGetTarget(out var cachedModel)) - { - return cachedModel; - } - - // oops. somebody has beaten me, but the model has gone. - // set me as new target - cached.SetTarget(speculativeSemanticModel); - return speculativeSemanticModel; - } - - private Dictionary GetVersionMapFromBranchOrPrimary(Workspace workspace, BranchId branchId) - { - var branchMap = GetBranchMap(workspace); - // check whether we already have one - if (branchMap.TryGetValue(branchId, out var versionMap)) - { - return versionMap; - } - - // check primary branch - if (branchMap.TryGetValue(workspace.PrimaryBranchId, out versionMap)) - { - return versionMap; - } - - // okay, create one - return branchMap.GetValue(branchId, s_createVersionMap); - } - - private Dictionary GetVersionMapFromBranch(Workspace workspace, BranchId branchId) - { - var branchMap = GetBranchMap(workspace); - - return branchMap.GetValue(branchId, s_createVersionMap); - } - - private ConditionalWeakTable> GetBranchMap(Workspace workspace) - { - if (!s_map.TryGetValue(workspace, out var branchMap)) - { - var newBranchMap = new ConditionalWeakTable>(); - - branchMap = s_map.GetValue(workspace, _ => newBranchMap); - if (branchMap == newBranchMap) - { - // it is first time we see this workspace. subscribe to it - workspace.DocumentClosed += OnDocumentClosed; - workspace.WorkspaceChanged += OnWorkspaceChanged; - } - } - - return branchMap; - } - - private void OnDocumentClosed(object? sender, DocumentEventArgs e) - => ClearVersionMap(e.Document.Project.Solution.Workspace, e.Document.Id); - - private void OnWorkspaceChanged(object? sender, WorkspaceChangeEventArgs e) - { - switch (e.Kind) - { - case WorkspaceChangeKind.SolutionAdded: - case WorkspaceChangeKind.SolutionChanged: - case WorkspaceChangeKind.SolutionRemoved: - case WorkspaceChangeKind.SolutionCleared: - case WorkspaceChangeKind.SolutionReloaded: - ClearVersionMap(e.NewSolution.Workspace, e.NewSolution.ProjectIds); - break; - case WorkspaceChangeKind.ProjectAdded: - case WorkspaceChangeKind.ProjectRemoved: - case WorkspaceChangeKind.ProjectChanged: - case WorkspaceChangeKind.ProjectReloaded: - ClearVersionMap(e.NewSolution.Workspace, e.ProjectId!); - break; - case WorkspaceChangeKind.DocumentRemoved: - ClearVersionMap(e.NewSolution.Workspace, e.DocumentId!); - break; - case WorkspaceChangeKind.DocumentAdded: - case WorkspaceChangeKind.DocumentReloaded: - case WorkspaceChangeKind.DocumentChanged: - case WorkspaceChangeKind.AdditionalDocumentAdded: - case WorkspaceChangeKind.AdditionalDocumentRemoved: - case WorkspaceChangeKind.AdditionalDocumentChanged: - case WorkspaceChangeKind.AdditionalDocumentReloaded: - case WorkspaceChangeKind.AnalyzerConfigDocumentAdded: - case WorkspaceChangeKind.AnalyzerConfigDocumentRemoved: - case WorkspaceChangeKind.AnalyzerConfigDocumentChanged: - case WorkspaceChangeKind.AnalyzerConfigDocumentReloaded: - break; - default: - throw ExceptionUtilities.UnexpectedValue(e.Kind); - } - } - - private void ClearVersionMap(Workspace workspace, DocumentId documentId) - { - if (workspace.GetOpenDocumentIds(documentId.ProjectId).Any()) - { - return; - } - - var versionMap = GetVersionMapFromBranch(workspace, workspace.PrimaryBranchId); - - using (_gate.DisposableWrite()) - { - versionMap.Remove(documentId.ProjectId); - } - } - - private void ClearVersionMap(Workspace workspace, ProjectId projectId) - { - var versionMap = GetVersionMapFromBranch(workspace, workspace.PrimaryBranchId); - - using (_gate.DisposableWrite()) - { - versionMap.Remove(projectId); - } - } - - private void ClearVersionMap(Workspace workspace, IReadOnlyList projectIds) - { - var versionMap = GetVersionMapFromBranch(workspace, workspace.PrimaryBranchId); - - using (_gate.DisposableWrite()) - { - using var pooledObject = SharedPools.Default>().GetPooledObject(); - var set = pooledObject.Object; - - set.UnionWith(versionMap.Keys); - set.ExceptWith(projectIds); - - foreach (var projectId in set) - { - versionMap.Remove(projectId); - } - } - } - - private sealed class CompilationSet - { - private const int RebuildThreshold = 3; - - public readonly VersionStamp Version; - public readonly ValueSource> Compilation; - public readonly ImmutableDictionary Trees; - - public static async Task CreateAsync(Project project, CompilationSet? oldCompilationSet, CancellationToken cancellationToken) - { - var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - - // project must support compilations - Contract.ThrowIfNull(compilation); - - var version = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - var map = GetTreeMap(project, compilation, oldCompilationSet, cancellationToken); - - ValidateTreeMap(map, project, compilation); - return new CompilationSet(version, GetCompilation(project, compilation), map); - } - - private CompilationSet(VersionStamp version, ValueSource> compilation, ImmutableDictionary map) - { - Version = version; - Compilation = compilation; - Trees = map; - } - - private static ImmutableDictionary GetTreeMap(Project project, Compilation compilation, CompilationSet? oldCompilationSet, CancellationToken cancellationToken) - { - // enumerable count should take a quick path since ImmutableArray implements ICollection - var newTreeCount = compilation.SyntaxTrees.Count(); - - // TODO: all this could go away if this is maintained by project itself and one can just get the map from it. - if (oldCompilationSet == null || Math.Abs(oldCompilationSet.Trees.Count - newTreeCount) > RebuildThreshold) - { - return ImmutableDictionary.CreateRange(GetNewTreeMap(project, compilation)); - } - - var map = AddOrUpdateNewTreeToOldMap(project, compilation, oldCompilationSet, cancellationToken); - - // check simple case. most of typing case should hit this. - // number of items in the map is same as number of new trees and old compilation doesn't have - // more trees than current one - if (map.Count == newTreeCount && oldCompilationSet.Trees.Count <= newTreeCount) - { - return map; - } - - // a bit more expensive case where there is a document in oldCompilationSet that doesn't exist in new compilation - return RemoveOldTreeFromMap(compilation, oldCompilationSet.Trees, map, cancellationToken); - } - - private static ImmutableDictionary RemoveOldTreeFromMap( - Compilation newCompilation, - ImmutableDictionary oldMap, ImmutableDictionary map, - CancellationToken cancellationToken) - { - foreach (var oldIdAndTree in oldMap) - { - cancellationToken.ThrowIfCancellationRequested(); - - // check whether new compilation still has the tree - if (newCompilation.ContainsSyntaxTree(oldIdAndTree.Value)) - { - continue; - } - - var documentId = oldIdAndTree.Key; - // check whether the tree has been updated - if (!map.TryGetValue(documentId, out var currentTree) || - currentTree != oldIdAndTree.Value) - { - continue; - } - - // this has been removed - map = map.Remove(documentId); - } - - return map; - } - - private static ImmutableDictionary AddOrUpdateNewTreeToOldMap( - Project newProject, Compilation newCompilation, CompilationSet oldSet, CancellationToken cancellationToken) - { - if (!oldSet.Compilation.TryGetValue(out var oldCompilationOpt) || !oldCompilationOpt.HasValue) - { - return ImmutableDictionary.CreateRange(GetNewTreeMap(newProject, newCompilation)); - } - - var map = oldSet.Trees; - foreach (var newTree in newCompilation.SyntaxTrees) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (oldCompilationOpt.Value.ContainsSyntaxTree(newTree)) - { - continue; - } - - var documentId = newProject.GetDocumentId(newTree); - - // GetDocumentId will return null for #load'ed trees. - // TODO: Remove this check and add logic to fetch the #load'ed tree's - // Document once https://github.com/dotnet/roslyn/issues/5260 is fixed. - if (documentId == null) - { - Debug.Assert(newProject.Solution.Workspace.Kind == WorkspaceKind.Interactive || newProject.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles); - continue; - } - - map = map.SetItem(documentId, newTree); - } - - return map; - } - - private static IEnumerable> GetNewTreeMap(Project project, Compilation compilation) - { - foreach (var tree in compilation.SyntaxTrees) - { - var documentId = project.GetDocumentId(tree); - if (documentId != null) - { - yield return KeyValuePairUtil.Create(documentId, tree); - } - } - } - - private static ValueSource> GetCompilation(Project project, Compilation compilation) - { - var cache = project.Solution.Workspace.Services.GetService(); - if (cache != null && project.Solution.BranchId == project.Solution.Workspace.PrimaryBranchId) - { - return new WeakValueSource(cache.CacheObjectIfCachingEnabledForKey(project.Id, project, compilation)); - } - - return new ConstantValueSource>(compilation); - } - - [Conditional("DEBUG")] - private static void ValidateTreeMap(ImmutableDictionary actual, Project project, Compilation compilation) - { - var expected = ImmutableDictionary.CreateRange(GetNewTreeMap(project, compilation)); - Debug.Assert(actual.SetEquals(expected)); - } - } - } - } -} diff --git a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs index f1ec96c808722..16f42fb00aa3d 100644 --- a/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs +++ b/src/Workspaces/CoreTest/CodeCleanup/CodeCleanupTests.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeCleanup; using Microsoft.CodeAnalysis.CodeCleanup.Providers; -using Microsoft.CodeAnalysis.SemanticModelWorkspaceService; +using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; @@ -230,18 +230,20 @@ End Class var semanticModel = await document.GetSemanticModelAsync(); var root = await document.GetSyntaxRootAsync(); var accessor = root.DescendantNodes().OfType().Last(); - var factory = new SemanticModelWorkspaceServiceFactory(); - var service = (ISemanticModelService)factory.CreateService(document.Project.Solution.Workspace.Services); - var newSemanticModel = await service.GetSemanticModelForNodeAsync(document, accessor, CancellationToken.None); + var newSemanticModel = await document.ReuseExistingSpeculativeModelAsync(accessor.Statements[0], CancellationToken.None); Assert.NotNull(newSemanticModel); + Assert.False(newSemanticModel.IsSpeculativeSemanticModel); + var newDocument = CreateDocument(code, LanguageNames.VisualBasic); var newRoot = await newDocument.GetSyntaxRootAsync(); var newAccessor = newRoot.DescendantNodes().OfType().Last(); root = root.ReplaceNode(accessor, newAccessor); document = document.WithSyntaxRoot(root); accessor = root.DescendantNodes().OfType().Last(); - newSemanticModel = await service.GetSemanticModelForNodeAsync(document, accessor, CancellationToken.None); + newSemanticModel = await document.ReuseExistingSpeculativeModelAsync(accessor.Statements[0], CancellationToken.None); Assert.NotNull(newSemanticModel); + Assert.True(newSemanticModel.IsSpeculativeSemanticModel); + var cleanDocument = await CodeCleaner.CleanupAsync(document); Assert.Equal(document, cleanDocument); } diff --git a/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs b/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs new file mode 100644 index 0000000000000..7e468a9be6d68 --- /dev/null +++ b/src/Workspaces/CoreTest/SemanticModelReuse/SemanticModelReuseTests.cs @@ -0,0 +1,368 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Test.Utilities; +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Microsoft.CodeAnalysis.UnitTests.SemanticModelReuse +{ + [UseExportProvider] + public class SemanticModelReuseTests + { + private static Document CreateDocument(string code, string language) + { + var solution = new AdhocWorkspace().CurrentSolution; + var projectId = ProjectId.CreateNewId(); + var project = solution.AddProject(projectId, "Project", "Project.dll", language).GetProject(projectId); + + return project.AddMetadataReference(TestReferences.NetFx.v4_0_30319.mscorlib) + .AddDocument("Document", SourceText.From(code)); + } + + #region C# tests + + [Fact] + public async Task NullBodyReturnsNormalSemanticModel1_CSharp() + { + var document = CreateDocument("", LanguageNames.CSharp); + + // trying to get a model for null should return a non-speculative model + var model = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); + Assert.False(model.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task NullBodyReturnsNormalSemanticModel2_CSharp() + { + var source = "class C { void M() { return; } }"; + var document = CreateDocument(source, LanguageNames.CSharp); + + // Even if we've primed things with a real location, getting a semantic model for null should return a + // non-speculative model. + var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + var model2 = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + Assert.False(model2.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task SameSyntaxTreeReturnsNonSpeculativeModel_CSharp() + { + var source = "class C { void M() { return; } }"; + var document = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. The next call will also use the + // same syntax tree, so it should get the same semantic model. + var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + var model2 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + Assert.False(model2.IsSpeculativeSemanticModel); + + // Should be the same models. + Assert.Equal(model1, model2); + + // Which also should be the normal model the document provides. + var actualModel = await document.GetSemanticModelAsync(); + Assert.Equal(model1, actualModel); + } + + [Fact] + public async Task InBodyEditShouldProduceCachedModel_CSharp() + { + var source = "class C { void M() { return; } }"; + var document1 = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From("class C { void M() { return null; } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task OutOfBodyEditShouldProduceFreshModel_CSharp() + { + var source = "class C { void M() { return; } }"; + var document1 = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From("class C { long M() { return; } }")); + + // We changed the return type, so we can't reuse the previous model. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model2.IsSpeculativeSemanticModel); + + var document3 = document2.WithText(SourceText.From("class C { long M() { return 0; } }")); + + // We are now again only editing a method body so we should be able to get a speculative model. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task MultipleBodyEditsShouldProduceFreshModel_CSharp() + { + var source = "class C { void M() { return; } }"; + var document1 = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From("class C { void M() { return 0; } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + var document3 = document1.WithText(SourceText.From("class C { void M() { return 1; } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_CSharp() + { + var source = "class C { int M { get { return 0; } } }"; + var document1 = CreateDocument(source, LanguageNames.CSharp); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From("class C { int M { get { return 1; } } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + var document3 = document1.WithText(SourceText.From("class C { int M { get { return 2; } } }")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + #endregion + + #region Visual Basic tests + + [Fact] + public async Task NullBodyReturnsNormalSemanticModel1_VisualBasic() + { + var document = CreateDocument("", LanguageNames.VisualBasic); + + // trying to get a model for null should return a non-speculative model + var model = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); + Assert.False(model.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task NullBodyReturnsNormalSemanticModel2_VisualBasic() + { + var source = @" +class C + sub M() + return + end sub +end class"; + var document = CreateDocument(source, LanguageNames.VisualBasic); + + // Even if we've primed things with a real location, getting a semantic model for null should return a + // non-speculative model. + var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + var model2 = await document.ReuseExistingSpeculativeModelAsync(null, CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + Assert.False(model2.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task SameSyntaxTreeReturnsNonSpeculativeModel_VisualBasic() + { + var source = @" +class C + sub M() + return + end sub +end class"; + var document = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. The next call will also use the + // same syntax tree, so it should get the same semantic model. + var model1 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + var model2 = await document.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + Assert.False(model2.IsSpeculativeSemanticModel); + + // Should be the same models. + Assert.Equal(model1, model2); + + // Which also should be the normal model the document provides. + var actualModel = await document.GetSemanticModelAsync(); + Assert.Equal(model1, actualModel); + } + + [Fact] + public async Task InBodyEditShouldProduceCachedModel_VisualBasic() + { + var source = @" +class C + sub M() + return + end sub +end class"; + var document1 = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(@" +class C + sub M() + return nothing + end sub +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task OutOfBodyEditShouldProduceFreshModel_VisualBasic() + { + var source1 = @" +class C + sub M() + return + end sub +end class"; + var document1 = CreateDocument(source1, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source1.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var source2 = @" +class C + function M() as long + return + end function +end class"; + var document2 = document1.WithText(SourceText.From(source2)); + + // We changed the return type, so we can't reuse the previous model. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source2.IndexOf("return"), CancellationToken.None); + Assert.False(model2.IsSpeculativeSemanticModel); + + var document3 = document2.WithText(SourceText.From(@" +class C + function M() as long + return 0 + end function +end class")); + + // We are now again only editing a method body so we should be able to get a speculative model. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source2.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task MultipleBodyEditsShouldProduceFreshModel_VisualBasic() + { + var source = @"class C + sub M() + return + end sub +end class"; + var document1 = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(@" +class C + sub M() + return 0 + end sub +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + var document3 = document1.WithText(SourceText.From(@" +class C + sub M() + return 1 + end sub +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + [Fact] + public async Task MultipleBodyEditsShouldProduceFreshModel_Accessor_VisualBasic() + { + var source = @" +class C + readonly property M as integer + get + return 0 + end get + end property +end class"; + var document1 = CreateDocument(source, LanguageNames.VisualBasic); + + // First call will prime the cache to point at the real semantic model. + var model1 = await document1.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.False(model1.IsSpeculativeSemanticModel); + + var document2 = document1.WithText(SourceText.From(@" +class C + readonly property M as integer + get + return 1 + end get + end property +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model2 = await document2.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model2.IsSpeculativeSemanticModel); + + var document3 = document1.WithText(SourceText.From(@" +class C + readonly property M as integer + get + return 2 + end get + end property +end class")); + + // This should be able to get a speculative model using the original model we primed the cache with. + var model3 = await document3.ReuseExistingSpeculativeModelAsync(source.IndexOf("return"), CancellationToken.None); + Assert.True(model3.IsSpeculativeSemanticModel); + } + + #endregion + } +} diff --git a/src/Workspaces/CoreTest/SymbolKeyTests.cs b/src/Workspaces/CoreTest/SymbolKeyTests.cs index f099dd77d1869..b15a2e60bd0c5 100644 --- a/src/Workspaces/CoreTest/SymbolKeyTests.cs +++ b/src/Workspaces/CoreTest/SymbolKeyTests.cs @@ -623,19 +623,67 @@ void goo() var document = workspace.AddDocument(project.Id, "testdocument", sourceText); var firstModel = await document.GetSemanticModelAsync(); - var tree1 = await document.GetSyntaxTreeAsync(); - var basemethod1 = tree1.FindTokenOnLeftOfPosition(position, CancellationToken.None).GetAncestor(); + + // Ensure we prime the reuse cache with the true semantic model. + var firstReusedModel = await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None); + Assert.False(firstReusedModel.IsSpeculativeSemanticModel); + + // Modify the document so we can use the old semantic model as a base. + var updated = sourceText.WithChanges(new TextChange(new TextSpan(position, 0), "insertion")); + workspace.TryApplyChanges(document.WithText(updated).Project.Solution); + + document = workspace.CurrentSolution.GetDocument(document.Id); + + // Now, the second time we try to get a speculative model, we should succeed. + var testModel = await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None); + Assert.True(testModel.IsSpeculativeSemanticModel); + + var xSymbol = testModel.LookupSymbols(position).First(s => s.Name == "x"); + + // This should not throw an exception. + Assert.NotEqual(default, SymbolKey.Create(xSymbol)); + } + + [Fact, WorkItem(11193, "https://github.com/dotnet/roslyn/issues/11193")] + public async Task TestGetInteriorSymbolsDoesNotCrashOnSpeculativeSemanticModel_InProperty() + { + var markup = @" +class C +{ + int Prop + { + get + { + System.Func lambda = () => + { + int x; + $$ + } + } + } +}"; + MarkupTestFile.GetPosition(markup, out var text, out int position); + + var sourceText = SourceText.From(text); + var workspace = new AdhocWorkspace(); + var project = workspace.AddProject("Test", LanguageNames.CSharp); + var document = workspace.AddDocument(project.Id, "testdocument", sourceText); + + var firstModel = await document.GetSemanticModelAsync(); + + // Ensure we prime the reuse cache with the true semantic model. + var firstReusedModel = await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None); + Assert.False(firstReusedModel.IsSpeculativeSemanticModel); // Modify the document so we can use the old semantic model as a base. var updated = sourceText.WithChanges(new TextChange(new TextSpan(position, 0), "insertion")); workspace.TryApplyChanges(document.WithText(updated).Project.Solution); document = workspace.CurrentSolution.GetDocument(document.Id); - var tree2 = await document.GetSyntaxTreeAsync(); - var basemethod2 = tree2.FindTokenOnLeftOfPosition(position, CancellationToken.None).GetAncestor(); - var service = CSharp.CSharpSemanticFactsService.Instance; - var m = service.TryGetSpeculativeSemanticModel(firstModel, basemethod1, basemethod2, out var testModel); + // Now, the second time we try to get a speculative model, we should succeed. + var testModel = await document.ReuseExistingSpeculativeModelAsync(position, CancellationToken.None); + Assert.True(testModel.IsSpeculativeSemanticModel); var xSymbol = testModel.LookupSymbols(position).First(s => s.Name == "x"); diff --git a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj index 1b48d1953d113..ec930bf5a8a0e 100644 --- a/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj +++ b/src/Workspaces/Remote/ServiceHub/Microsoft.CodeAnalysis.Remote.ServiceHub.csproj @@ -16,7 +16,6 @@ - diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_CodeLens.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_CodeLens.cs index bc17a1c696a38..860788c9b5e04 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_CodeLens.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_CodeLens.cs @@ -240,15 +240,15 @@ async Task InvalidateAsync(Task _) return; } - // fire and forget. - // ignore any exception such as rpc already disposed (disconnected) - _lastVersion = newVersion; - await _endPoint.InvokeAsync( + // fire and forget. + // ignore any exception such as rpc already disposed (disconnected) + // fire and forget: + _ = _endPoint.TryInvokeAsync( nameof(IRemoteCodeLensDataPoint.Invalidate), Array.Empty(), - CancellationToken.None).ConfigureAwait(false); + _cancellationToken); } } } diff --git a/src/Workspaces/Remote/ServiceHub/Shared/RemoteEndPoint.cs b/src/Workspaces/Remote/ServiceHub/Shared/RemoteEndPoint.cs index 5c17d46517b83..3d8f65e864c52 100644 --- a/src/Workspaces/Remote/ServiceHub/Shared/RemoteEndPoint.cs +++ b/src/Workspaces/Remote/ServiceHub/Shared/RemoteEndPoint.cs @@ -119,6 +119,25 @@ public async Task InvokeAsync(string targetName, IReadOnlyList argument } } + public async Task TryInvokeAsync(string targetName, IReadOnlyList arguments, CancellationToken cancellationToken) + { + Contract.ThrowIfFalse(_startedListening); + + if (_rpc.IsDisposed) + { + return; + } + + try + { + await _rpc.InvokeWithCancellationAsync(targetName, arguments, cancellationToken).ConfigureAwait(false); + } + catch + { + // ignore + } + } + public async Task InvokeAsync(string targetName, IReadOnlyList arguments, CancellationToken cancellationToken) { Contract.ThrowIfFalse(_startedListening); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems index 620745a3883dc..c11693b00b67c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/CSharpCompilerExtensions.projitems @@ -20,6 +20,7 @@ + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs new file mode 100644 index 0000000000000..9d54363a849d2 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -0,0 +1,40 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Utilities; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +#pragma warning disable IDE0060 // Remove unused parameter - Majority of extension methods in this file have an unused 'SyntaxTree' this parameter for consistency with other Context related extension methods. + +namespace Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery +{ + internal static partial class SyntaxTreeExtensions + { + public static bool IsPreProcessorDirectiveContext(this SyntaxTree syntaxTree, int position, SyntaxToken preProcessorTokenOnLeftOfPosition, CancellationToken cancellationToken) + { + var token = preProcessorTokenOnLeftOfPosition; + var directive = token.GetAncestor(); + + // Directives contain the EOL, so if the position is within the full span of the + // directive, then it is on that line, the only exception is if the directive is on the + // last line, the position at the end if technically not contained by the directive but + // its also not on a new line, so it should be considered part of the preprocessor + // context. + if (directive == null) + { + return false; + } + + return + directive.FullSpan.Contains(position) || + directive.FullSpan.End == syntaxTree.GetRoot(cancellationToken).FullSpan.End; + } + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 98b7bd68d2d1d..8e0a9ac08ff24 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -19,6 +19,7 @@ using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; using Microsoft.CodeAnalysis.CSharp.Formatting; +using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery; #if CODE_STYLE using Microsoft.CodeAnalysis.Internal.Editing; @@ -102,6 +103,10 @@ public bool IsContextualKeyword(SyntaxToken token) public bool IsPreprocessorKeyword(SyntaxToken token) => SyntaxFacts.IsPreprocessorKeyword(token.Kind()); + public bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) + => syntaxTree.IsPreProcessorDirectiveContext( + position, syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); + public bool IsEntirelyWithinStringOrCharOrNumericLiteral(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { if (syntaxTree == null) @@ -978,71 +983,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 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) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 909e42c07406a..5e919d9972423 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -81,6 +81,7 @@ internal partial interface ISyntaxFacts /// preprocessor directive. For example `if` or `pragma`. /// bool IsPreprocessorKeyword(SyntaxToken token); + bool IsPreProcessorDirectiveContext(SyntaxTree syntaxTree, int position, CancellationToken cancellationToken); bool IsLiteral(SyntaxToken token); bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token); @@ -374,8 +375,6 @@ void GetPartsOfTupleExpression(SyntaxNode node, SyntaxList GetMembersOfCompilationUnit(SyntaxNode compilationUnit); bool ContainsInMemberBody(SyntaxNode node, TextSpan span); - int GetMethodLevelMemberId(SyntaxNode root, SyntaxNode node); - SyntaxNode GetMethodLevelMember(SyntaxNode root, int memberId); TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree tree, int position, CancellationToken cancellationToken); /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 345e2226384e6..b117fcd5d5194 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -108,6 +108,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Return token.IsPreprocessorKeyword() End Function + Public Function IsPreProcessorDirectiveContext(syntaxTree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISyntaxFacts.IsPreProcessorDirectiveContext + Return syntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) + End Function + Public Function TryGetCorrespondingOpenBrace(token As SyntaxToken, ByRef openBrace As SyntaxToken) As Boolean Implements ISyntaxFacts.TryGetCorrespondingOpenBrace If token.Kind = SyntaxKind.CloseBraceToken Then @@ -1065,66 +1069,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 - - - 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 diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs index a0e154e46f5e5..0a75199c4f1a9 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/Extensions/ContextQuery/SyntaxTreeExtensions.cs @@ -1885,26 +1885,6 @@ private static bool IsNonConstantExpressionContext(SyntaxTree syntaxTree, int po !syntaxTree.IsConstantExpressionContext(position, tokenOnLeftOfPosition); } - public static bool IsPreProcessorDirectiveContext(this SyntaxTree syntaxTree, int position, SyntaxToken preProcessorTokenOnLeftOfPosition, CancellationToken cancellationToken) - { - var token = preProcessorTokenOnLeftOfPosition; - var directive = token.GetAncestor(); - - // Directives contain the EOL, so if the position is within the full span of the - // directive, then it is on that line, the only exception is if the directive is on the - // last line, the position at the end if technically not contained by the directive but - // its also not on a new line, so it should be considered part of the preprocessor - // context. - if (directive == null) - { - return false; - } - - return - directive.FullSpan.Contains(position) || - directive.FullSpan.End == syntaxTree.GetRoot(cancellationToken).FullSpan.End; - } - public static bool IsPreProcessorDirectiveContext(this SyntaxTree syntaxTree, int position, CancellationToken cancellationToken) { var leftToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs index 19dd5b066002e..b5b0f3e936e93 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/CSharp/LanguageServices/CSharpSemanticFactsService.cs @@ -99,12 +99,6 @@ public bool IsMemberDeclarationContext(SemanticModel semanticModel, int position position, semanticModel.SyntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken)); } - public bool IsPreProcessorDirectiveContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken) - { - return semanticModel.SyntaxTree.IsPreProcessorDirectiveContext( - position, semanticModel.SyntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives: true), cancellationToken); - } - public bool IsGlobalStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken) => semanticModel.SyntaxTree.IsGlobalStatementContext(position, cancellationToken); @@ -181,22 +175,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 GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken) { var original = model.GetOriginalSemanticModel(); diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs index e4a5b9310dfe4..0918b004bb980 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/Extensions/DocumentExtensions.cs @@ -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; @@ -65,74 +65,70 @@ public static bool IsOpen(this Document document) } /// - /// this will return either regular semantic model or speculative semantic based on context. - /// any feature that is involved in typing or run on UI thread should use this to take advantage of speculative semantic model - /// whenever possible automatically. - /// - /// when using this API, semantic model should only be used to ask node inside of the given span. - /// otherwise, it might throw if semantic model returned by this API is a speculative semantic model. - /// - /// also, symbols from the semantic model returned by this API might have out of date location information. - /// if exact location (not relative location) is needed from symbol, regular GetSemanticModel should be used. + /// Attempts to return an speculative semantic model for if possible if is contained within a method body in the tree. Specifically, this will attempt to get an + /// existing cached semantic model for . If it can find one, and the top-level semantic + /// version for this project matches the cached version, and the position is within a method body, then it will + /// be returned, just with the previous corresponding method body swapped out with the current method body. + /// + /// If this is not possible, the regular semantic model for will be returned. + /// + /// When using this API, semantic model should only be used to ask questions about nodes inside of the member + /// that contains the given . + /// + /// As a speculative semantic model may be returned, location based information provided by it may be innacurate. /// - public static async Task GetSemanticModelForSpanAsync(this Document document, TextSpan span, CancellationToken cancellationToken) + public static Task ReuseExistingSpeculativeModelAsync(this Document document, int position, CancellationToken cancellationToken) + => ReuseExistingSpeculativeModelAsync(document, new TextSpan(position, 0), cancellationToken); + + /// + /// Attempts to return an speculative semantic model for if possible if is contained within a method body in the tree. Specifically, this will attempt to get an + /// existing cached semantic model . If it can find one, and the top-level semantic + /// version for this project matches the cached version, and the position is within a method body, then it will + /// be returned, just with the previous corresponding method body swapped out with the current method body. + /// + /// If this is not possible, the regular semantic model for will be returned. + /// + /// When using this API, semantic model should only be used to ask questions about nodes inside of the + /// member that contains the given . + /// + /// As a speculative semantic model may be returned, location based information provided by it may be innacurate. + /// + public static async Task ReuseExistingSpeculativeModelAsync(this Document document, TextSpan span, CancellationToken cancellationToken) { Contract.ThrowIfFalse(document.SupportsSemanticModel); - var syntaxFactService = document.GetLanguageService(); - var semanticModelService = document.Project.Solution.Workspace.Services.GetService(); - if (semanticModelService == null || syntaxFactService == null) - { - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } - - var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - Contract.ThrowIfNull(root, "We shouldn't have a null root if the document supports semantic models"); + var root = await document.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var token = root.FindToken(span.Start); - if (token.Parent == null) - { - return (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!; - } + var node = token.Parent!.AncestorsAndSelf().First(a => a.FullSpan.Contains(span)); - var node = token.Parent.AncestorsAndSelf().First(a => a.FullSpan.Contains(span)); - return await GetSemanticModelForNodeAsync(semanticModelService, syntaxFactService, document, node, span, cancellationToken).ConfigureAwait(false); + return await ReuseExistingSpeculativeModelAsync(document, node, cancellationToken).ConfigureAwait(false); } /// - /// this will return either regular semantic model or speculative semantic based on context. - /// any feature that is involved in typing or run on UI thread should use this to take advantage of speculative semantic model - /// whenever possible automatically. - /// - /// when using this API, semantic model should only be used to ask node inside of the given node except ones that belong to - /// member signature. otherwise, it might throw if semantic model returned by this API is a speculative semantic model. - /// - /// also, symbols from the semantic model returned by this API might have out of date location information. - /// if exact location (not relative location) is needed from symbol, regular GetSemanticModel should be used. + /// Attempts to return an speculative semantic model for if possible if is contained within a method body in the tree. Specifically, this will attempt to get an + /// existing cached semantic model . If it can find one, and the top-level semantic + /// version for this project matches the cached version, and the position is within a method body, then it will + /// be returned, just with the previous corresponding method body swapped out with the current method body. + /// + /// If this is not possible, the regular semantic model for will be returned. + /// + /// When using this API, semantic model should only be used to ask questions about nodes inside of the + /// member that contains the given . + /// + /// As a speculative semantic model may be returned, location based information provided by it may be innacurate. /// - public static Task GetSemanticModelForNodeAsync(this Document document, SyntaxNode? node, CancellationToken cancellationToken) + public static Task ReuseExistingSpeculativeModelAsync(this Document document, SyntaxNode? node, CancellationToken cancellationToken) { - var syntaxFactService = document.GetLanguageService(); - var semanticModelService = document.Project.Solution.Workspace.Services.GetService(); - if (semanticModelService == null || syntaxFactService == null || node == null) - { - return document.GetSemanticModelAsync(cancellationToken)!; - } - - return GetSemanticModelForNodeAsync(semanticModelService, syntaxFactService, document, node, node.FullSpan, cancellationToken); - } + if (node == null) + return document.GetRequiredSemanticModelAsync(cancellationToken); - private static Task GetSemanticModelForNodeAsync( - ISemanticModelService semanticModelService, ISyntaxFactsService syntaxFactService, - Document document, SyntaxNode node, TextSpan span, CancellationToken cancellationToken) - { - // check whether given span is a valid span to do speculative binding - var speculativeBindingSpan = syntaxFactService.GetMemberBodySpanForSpeculativeBinding(node); - if (!speculativeBindingSpan.Contains(span)) - { - return document.GetSemanticModelAsync(cancellationToken)!; - } + var workspace = document.Project.Solution.Workspace; + var semanticModelService = workspace.Services.GetRequiredService(); - return semanticModelService.GetSemanticModelForNodeAsync(document, node, cancellationToken); + return semanticModelService.ReuseExistingSpeculativeModelAsync(document, node, cancellationToken); } #if DEBUG diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs index 4e3a2f227a7e0..eb41ad759b346 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/LanguageServices/SemanticsFactsService/ISemanticFactsService.cs @@ -48,7 +48,6 @@ internal interface ISemanticFactsService : ILanguageService bool IsNamespaceDeclarationNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); bool IsTypeDeclarationContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); bool IsMemberDeclarationContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); - bool IsPreProcessorDirectiveContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); bool IsGlobalStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); bool IsLabelContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); bool IsAttributeNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken); @@ -80,11 +79,6 @@ internal interface ISemanticFactsService : ILanguageService bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol); - /// - /// return speculative semantic model for supported node. otherwise, it will return null - /// - bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel); - /// /// get all alias names defined in the semantic model /// diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems index 268a30e5d409b..2b9e49214e776 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceExtensions.projitems @@ -64,8 +64,8 @@ - - + + diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/ISemanticModelService.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/ISemanticModelReuseWorkspaceService.cs similarity index 52% rename from src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/ISemanticModelService.cs rename to src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/ISemanticModelReuseWorkspaceService.cs index 9b105a12ee9c9..8f25d968358bd 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/ISemanticModelService.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/ISemanticModelReuseWorkspaceService.cs @@ -7,20 +7,19 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Extensions; -namespace Microsoft.CodeAnalysis.SemanticModelWorkspaceService +namespace Microsoft.CodeAnalysis.SemanticModelReuse { /// /// a service that provides a semantic model that will re-use last known compilation if /// semantic version hasn't changed. /// - internal interface ISemanticModelService : IWorkspaceService + internal interface ISemanticModelReuseWorkspaceService : IWorkspaceService { /// - /// Don't call this directly. use Document extension method GetSemanticModelForNodeAsync or GetSemanticModelForSpanAsync instead. - /// - /// see the descriptions on the extension methods + /// Don't call this directly. use (or an overload). /// - Task GetSemanticModelForNodeAsync(Document document, SyntaxNode node, CancellationToken cancellationToken); + Task ReuseExistingSpeculativeModelAsync(Document document, SyntaxNode node, CancellationToken cancellationToken); } } diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs new file mode 100644 index 0000000000000..18a533a5ea315 --- /dev/null +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelReuse/SemanticModelReuseWorkspaceServiceFactory.cs @@ -0,0 +1,27 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +#nullable enable + +using System; +using System.Composition; +using System.Diagnostics.CodeAnalysis; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.SemanticModelReuse +{ + [ExportWorkspaceServiceFactory(typeof(ISemanticModelReuseWorkspaceService), ServiceLayer.Default), Shared] + internal partial class SemanticModelReuseWorkspaceServiceFactory : IWorkspaceServiceFactory + { + [ImportingConstructor] + [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] + public SemanticModelReuseWorkspaceServiceFactory() + { + } + + public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) + => new SemanticModelReuseWorkspaceService(workspaceServices.Workspace); + } +} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.cs deleted file mode 100644 index 903c93e0a261a..0000000000000 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/Core/WorkspaceServices/SemanticModelWorkspaceService/SemanticModelWorkspaceServiceFactory.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -#nullable enable - -using System.Composition; -using System.Diagnostics.CodeAnalysis; -using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Host.Mef; - -namespace Microsoft.CodeAnalysis.SemanticModelWorkspaceService -{ - [ExportWorkspaceServiceFactory(typeof(ISemanticModelService), ServiceLayer.Default), Shared] - internal partial class SemanticModelWorkspaceServiceFactory : IWorkspaceServiceFactory - { - [ImportingConstructor] - [SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")] - public SemanticModelWorkspaceServiceFactory() - { - } - - public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new SemanticModelService(); - } -} diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb index aa6eeab5496d2..22a76b9b18934 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Workspace/VisualBasic/LanguageServices/VisualBasicSemanticFactsService.vb @@ -98,10 +98,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return semanticModel.SyntaxTree.IsTypeDeclarationContext(position, token, cancellationToken) End Function - Public Function IsPreProcessorDirectiveContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsPreProcessorDirectiveContext - Return semanticModel.SyntaxTree.IsInPreprocessorDirectiveContext(position, cancellationToken) - End Function - Public Function IsGlobalStatementContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsGlobalStatementContext Return False End Function @@ -200,42 +196,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Function TryGetSpeculativeSemanticModel(oldSemanticModel As SemanticModel, oldNode As SyntaxNode, newNode As SyntaxNode, 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) diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb index 582216a546b29..d8ec5e1163e24 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/AddMissingTokensCodeCleanupProvider.vb @@ -44,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Public Shared Async Function CreateAsync(document As Document, spans As ImmutableArray(Of TextSpan), cancellationToken As CancellationToken) As Task(Of AddMissingTokensRewriter) Dim modifiedSpan = spans.Collapse() Dim semanticModel = If(document Is Nothing, Nothing, - Await document.GetSemanticModelForSpanAsync(modifiedSpan, cancellationToken).ConfigureAwait(False)) + Await document.ReuseExistingSpeculativeModelAsync(modifiedSpan, cancellationToken).ConfigureAwait(False)) Return New AddMissingTokensRewriter(semanticModel, spans, cancellationToken) End Function diff --git a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb index f69c83a558401..5cf2a1980917f 100644 --- a/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb +++ b/src/Workspaces/VisualBasic/Portable/CodeCleanup/Providers/FixIncorrectTokensCodeCleanupProvider.vb @@ -56,7 +56,7 @@ Namespace Microsoft.CodeAnalysis.CodeCleanup.Providers Dim modifiedSpan = spans.Collapse() Dim semanticModel = If(document Is Nothing, Nothing, - Await document.GetSemanticModelForSpanAsync(modifiedSpan, cancellationToken).ConfigureAwait(False)) + Await document.ReuseExistingSpeculativeModelAsync(modifiedSpan, cancellationToken).ConfigureAwait(False)) Return New FixIncorrectTokensRewriter(semanticModel, spans, cancellationToken) End Function diff --git a/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb b/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb new file mode 100644 index 0000000000000..620fe5390d3ad --- /dev/null +++ b/src/Workspaces/VisualBasic/Portable/SemanticModelReuse/VisualBasicSemanticModelReuseLanguageService.vb @@ -0,0 +1,69 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports System.Composition +Imports System.Threading +Imports Microsoft.CodeAnalysis.Host.Mef +Imports Microsoft.CodeAnalysis.LanguageServices +Imports Microsoft.CodeAnalysis.SemanticModelReuse +Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.SemanticModelReuse + + Friend Class VisualBasicSemanticModelReuseLanguageService + Inherits AbstractSemanticModelReuseLanguageService(Of + MethodBlockBaseSyntax, AccessorBlockSyntax, PropertyBlockSyntax, EventBlockSyntax) + + + + Public Sub New() + End Sub + + Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance + + Protected Overrides Function GetAccessors([event] As EventBlockSyntax) As SyntaxList(Of AccessorBlockSyntax) + Return [event].Accessors + End Function + + Protected Overrides Function GetAccessors([property] As PropertyBlockSyntax) As SyntaxList(Of AccessorBlockSyntax) + Return [property].Accessors + End Function + + Public Overrides Function TryGetContainingMethodBodyForSpeculation(node As SyntaxNode) As SyntaxNode + Dim previous = node + While node IsNot Nothing + Dim methodBlock = TryCast(node, MethodBlockBaseSyntax) + If methodBlock IsNot Nothing Then + Return If(methodBlock.Statements.Contains(TryCast(previous, StatementSyntax)), methodBlock, Nothing) + End If + + previous = node + node = node.Parent + End While + + Return Nothing + End Function + + Protected Overrides Async Function TryGetSpeculativeSemanticModelWorkerAsync( + previousSemanticModel As SemanticModel, currentBodyNode As SyntaxNode, cancellationToken As CancellationToken) As Task(Of SemanticModel) + + Dim previousRoot = Await previousSemanticModel.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(False) + Dim currentRoot = Await currentBodyNode.SyntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(False) + + Dim previousBodyNode = TryCast(GetPreviousBodyNode(previousRoot, currentRoot, currentBodyNode), MethodBlockBaseSyntax) + If previousBodyNode Is Nothing Then + Debug.Fail("Could not map current body to previous body, despite no top level changes") + Return Nothing + End If + + Dim speculativeModel As SemanticModel = Nothing + If previousSemanticModel.TryGetSpeculativeSemanticModelForMethodBody(previousBodyNode.BlockStatement.FullSpan.End, DirectCast(currentBodyNode, MethodBlockBaseSyntax), speculativeModel) Then + Return speculativeModel + End If + + Return Nothing + End Function + End Class +End Namespace