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.43.6.0-2.final1.0.1-beta1.20210.23.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 @@
-
-
- Index se nepodařilo stáhnout.
-
-
-
-
- Index se nepodařilo stáhnout: {0}.
-
-
-
-
- Stahuje se index IntelliSense pro {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 @@
-
-
- Fehler beim Herunterladen des Index.
-
-
-
-
- Fehler beim Herunterladen von Index: {0}.
-
-
-
-
- Der IntelliSense-Index für "{0}" wird heruntergeladen.
-
- 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 @@
-
-
- Error al descargar el índice.
-
-
-
-
- Error al descargar el índice: {0}
-
-
-
-
- Descargando el índice de IntelliSense para {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 @@
-
-
- Échec du téléchargement de l'index
-
-
-
-
- Échec du téléchargement de l'index : {0}
-
-
-
-
- Téléchargement de l'index IntelliSense pour {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 @@
-
-
- Il download dell'indice non è riuscito
-
-
-
-
- Il download dell'indice non è riuscito: {0}
-
-
-
-
- Download dell'indice IntelliSense per {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 @@
-
-
- インデックスのダウンロードに失敗しました
-
-
-
-
- インデックスのダウンロードに失敗しました: {0}
-
-
-
-
- {0} の IntelliSense インデックスをダウンロードしています
-
- 提案を収集しています - '{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 @@
-
-
- 인덱스를 다운로드하지 못했습니다.
-
-
-
-
- 인덱스 다운로드 실패:{0}
-
-
-
-
- {0}의 IntelliSense 인덱스 다운로드 중
-
- 제안을 수집하는 중 - '{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 @@
-
-
- Pobieranie indeksu nie powiodło się
-
-
-
-
- Pobieranie indeksu nie powiodło się: {0}
-
-
-
-
- Pobieranie indeksu funkcji IntelliSense dla {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 @@
-
-
- Falha ao baixar o índice
-
-
-
-
- Falha ao baixar o índice:{0}
-
-
-
-
- Baixando o índice do IntelliSense para {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 @@
-
-
- Не удалось скачать индекс.
-
-
-
-
- Ошибка при скачивании индекса: {0}
-
-
-
-
- Загрузка индекса IntelliSense для {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 @@
-
-
- Dizin indirilemedi
-
-
-
-
- Dizin indirilemedi: {0}
-
-
-
-
- {0} için IntelliSense dizini indiriliyor
-
- Ö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 @@
-
-
- 下载索引失败
-
-
-
-
- 下载索引失败: {0}
-
-
-
-
- 正在下载用于 {0} 的 IntelliSense 索引
-
- 正在收集建议 -“{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 @@
-
-
- 下載索引失敗
-
-
-
-
- 下載索引失敗: {0}
-
-
-
-
- 正在為 {0} 下載 IntelliSense 索引
-
- 正在蒐集建議 - '{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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ é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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ インポートされていない名前空間の項目
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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 가져오지 않은 네임스페이스의 항목
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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ элементы из неимпортированных пространств имен
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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 未导入命名空间中的项
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 index failed
+
+
+
+
+ Downloading index failed:{0}
+
+ 來自未匯入命名空間的項目
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