diff --git a/eng/Versions.props b/eng/Versions.props
index 1afcd5e1d0dae..fae37428d1527 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -36,7 +36,7 @@
17.0.487
5.0.0-alpha1.19409.1
5.0.0-preview.1.20112.8
- 17.0.5133-g7b8c8bd49d
+ 17.1.3
17.0.31723.112
16.5.0
- 3.3.2
+ 3.3.3
2.0.0-rc2-61102-09
$(MicrosoftCodeAnalysisTestingVersion)
$(MicrosoftCodeAnalysisTestingVersion)
diff --git a/eng/config/globalconfigs/Common.globalconfig b/eng/config/globalconfigs/Common.globalconfig
index 9be4ffd974176..7dc808dcb31fd 100644
--- a/eng/config/globalconfigs/Common.globalconfig
+++ b/eng/config/globalconfigs/Common.globalconfig
@@ -28,6 +28,10 @@ dotnet_diagnostic.RS1022.severity = none
# RS1024: Compare symbols correctly
dotnet_diagnostic.RS1024.severity = refactoring
+# RS1034: Prefer 'IsKind' for checking syntax kinds
+# Hold on https://github.com/dotnet/roslyn/pull/51823#pullrequestreview-612747491
+dotnet_diagnostic.RS1034.severity = none
+
dotnet_diagnostic.AD0001.severity = error
dotnet_diagnostic.RS0001.severity = warning
diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
index 7506cb89b7456..95f7b6758aec2 100644
--- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
@@ -1931,7 +1931,7 @@ internal void GenerateAnonymousFunctionConversionError(BindingDiagnosticBag diag
}
if (anonymousFunction.FunctionType is { } functionType &&
- functionType.GetValue() is null)
+ functionType.GetInternalDelegateType() is null)
{
var discardedUseSiteInfo = CompoundUseSiteInfo.Discarded;
if (Conversions.IsValidFunctionTypeConversionTarget(targetType, ref discardedUseSiteInfo))
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
index 89ebc123adfda..7fc47ca82f16f 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/BestTypeInferrer.cs
@@ -91,8 +91,9 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ
if (result is FunctionTypeSymbol functionType)
{
- inferredFromFunctionType = true;
- return functionType.GetInternalDelegateType();
+ result = functionType.GetInternalDelegateType();
+ inferredFromFunctionType = result is { };
+ return result;
}
inferredFromFunctionType = false;
@@ -182,14 +183,18 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ
case 0:
return null;
case 1:
- return types[0];
+ return checkType(types[0]);
}
TypeSymbol? best = null;
int bestIndex = -1;
for (int i = 0; i < types.Count; i++)
{
- TypeSymbol type = types[i];
+ TypeSymbol? type = checkType(types[i]);
+ if (type is null)
+ {
+ continue;
+ }
if (best is null)
{
best = type;
@@ -220,7 +225,11 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ
// that every type *before* best was also worse.
for (int i = 0; i < bestIndex; i++)
{
- TypeSymbol type = types[i];
+ TypeSymbol? type = checkType(types[i]);
+ if (type is null)
+ {
+ continue;
+ }
TypeSymbol? better = Better(best, type, conversions, ref useSiteInfo);
if (!best.Equals(better, TypeCompareKind.IgnoreNullableModifiersForReferenceTypes))
{
@@ -229,6 +238,11 @@ public static NullableFlowState GetNullableState(ArrayBuilder typ
}
return best;
+
+ static TypeSymbol? checkType(TypeSymbol type) =>
+ type is FunctionTypeSymbol functionType && functionType.GetInternalDelegateType() is null ?
+ null :
+ type;
}
///
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs
index ee552683aa453..c5c81078d7683 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs
@@ -2669,7 +2669,8 @@ private bool HasImplicitFunctionTypeConversion(FunctionTypeSymbol source, TypeSy
return HasImplicitFunctionTypeToFunctionTypeConversion(source, destinationFunctionType, ref useSiteInfo);
}
- return IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo);
+ return IsValidFunctionTypeConversionTarget(destination, ref useSiteInfo) &&
+ source.GetInternalDelegateType() is { };
}
internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo)
@@ -2697,7 +2698,16 @@ internal bool IsValidFunctionTypeConversionTarget(TypeSymbol destination, ref Co
private bool HasImplicitFunctionTypeToFunctionTypeConversion(FunctionTypeSymbol sourceType, FunctionTypeSymbol destinationType, ref CompoundUseSiteInfo useSiteInfo)
{
var sourceDelegate = sourceType.GetInternalDelegateType();
+ if (sourceDelegate is null)
+ {
+ return false;
+ }
+
var destinationDelegate = destinationType.GetInternalDelegateType();
+ if (destinationDelegate is null)
+ {
+ return false;
+ }
// https://github.com/dotnet/roslyn/issues/55909: We're relying on the variance of
// FunctionTypeSymbol.GetInternalDelegateType() which fails for synthesized
@@ -2848,13 +2858,14 @@ private bool HasAnyBaseInterfaceConversion(TypeSymbol derivedType, TypeSymbol ba
// * if the ith parameter of U is contravariant then either Si is exactly
// equal to Ti, or there is an implicit reference conversion from Ti to Si.
+#nullable enable
private bool HasInterfaceVarianceConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo useSiteInfo)
{
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
- NamedTypeSymbol s = source as NamedTypeSymbol;
- NamedTypeSymbol d = destination as NamedTypeSymbol;
- if ((object)s == null || (object)d == null)
+ NamedTypeSymbol? s = source as NamedTypeSymbol;
+ NamedTypeSymbol? d = destination as NamedTypeSymbol;
+ if (s is null || d is null)
{
return false;
}
@@ -2871,9 +2882,9 @@ private bool HasDelegateVarianceConversion(TypeSymbol source, TypeSymbol destina
{
Debug.Assert((object)source != null);
Debug.Assert((object)destination != null);
- NamedTypeSymbol s = source as NamedTypeSymbol;
- NamedTypeSymbol d = destination as NamedTypeSymbol;
- if ((object)s == null || (object)d == null)
+ NamedTypeSymbol? s = source as NamedTypeSymbol;
+ NamedTypeSymbol? d = destination as NamedTypeSymbol;
+ if (s is null || d is null)
{
return false;
}
@@ -3150,7 +3161,6 @@ internal static bool HasImplicitPointerToVoidConversion(TypeSymbol source, TypeS
return source.IsPointerOrFunctionPointer() && destination is PointerTypeSymbol { PointedAtType: { SpecialType: SpecialType.System_Void } };
}
-#nullable enable
internal bool HasImplicitPointerConversion(TypeSymbol? source, TypeSymbol? destination, ref CompoundUseSiteInfo useSiteInfo)
{
if (!(source is FunctionPointerTypeSymbol { Signature: { } sourceSig })
diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
index 3d6df87fe2654..41cd2ec1072b8 100644
--- a/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
+++ b/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs
@@ -2664,9 +2664,12 @@ private static (TypeWithAnnotations Type, bool FromFunctionType) Fix(
if (containsFunctionTypes(lower) &&
(containsNonFunctionTypes(lower) || containsNonFunctionTypes(exact) || containsNonFunctionTypes(upper)))
{
- lower = removeFunctionTypes(lower);
+ lower = removeTypes(lower, static type => isFunctionType(type, out _));
}
+ // Remove any function types with no delegate type.
+ lower = removeTypes(lower, static type => isFunctionType(type, out var functionType) && functionType.GetInternalDelegateType() is null);
+
// Optimization: if we have one exact bound then we need not add any
// inexact bounds; we're just going to remove them anyway.
@@ -2806,12 +2809,16 @@ static bool isExpressionType(TypeSymbol? type)
return false;
}
- static HashSet? removeFunctionTypes(HashSet types)
+ static HashSet? removeTypes(HashSet? types, Func predicate)
{
+ if (types is null)
+ {
+ return null;
+ }
HashSet? updated = null;
foreach (var type in types)
{
- if (!isFunctionType(type, out _))
+ if (!predicate(type))
{
updated ??= new HashSet(TypeWithAnnotations.EqualsComparer.ConsiderEverythingComparer);
updated.Add(type);
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs
index cf855070b5234..34d94060b1d9c 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpressionExtensions.cs
@@ -111,13 +111,12 @@ public static bool HasDynamicType(this BoundExpression node)
public static FunctionTypeSymbol? GetFunctionType(this BoundExpression expr)
{
- var lazyType = expr switch
+ return expr switch
{
BoundMethodGroup methodGroup => methodGroup.FunctionType,
UnboundLambda unboundLambda => unboundLambda.FunctionType,
_ => null
};
- return lazyType?.GetValue();
}
public static bool MethodGroupReceiverIsDynamic(this BoundMethodGroup node)
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs
index e9cd966be0d25..01b94d33dbbef 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundMethodGroup.cs
@@ -20,14 +20,14 @@ public BoundMethodGroup(
BoundMethodGroupFlags flags,
Binder binder,
bool hasErrors = false)
- : this(syntax, typeArgumentsOpt, name, methods, lookupResult.SingleSymbolOrDefault, lookupResult.Error, flags, functionType: GetLazyFunctionType(binder, syntax), receiverOpt, lookupResult.Kind, hasErrors)
+ : this(syntax, typeArgumentsOpt, name, methods, lookupResult.SingleSymbolOrDefault, lookupResult.Error, flags, functionType: GetFunctionType(binder, syntax), receiverOpt, lookupResult.Kind, hasErrors)
{
FunctionType?.SetExpression(this);
}
- private static FunctionTypeSymbol.Lazy? GetLazyFunctionType(Binder binder, SyntaxNode syntax)
+ private static FunctionTypeSymbol? GetFunctionType(Binder binder, SyntaxNode syntax)
{
- return FunctionTypeSymbol.Lazy.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => binder.GetMethodGroupDelegateType((BoundMethodGroup)expr));
+ return FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => binder.GetMethodGroupDelegateType((BoundMethodGroup)expr));
}
public MemberAccessExpressionSyntax? MemberAccessExpressionSyntax
diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
index a41f9b11afcd0..8f5c584ea5641 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
+++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
@@ -1704,7 +1704,7 @@
-
+
@@ -2117,7 +2117,7 @@
-
+
diff --git a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
index 20f4e77f084f9..91d12ba91a738 100644
--- a/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
+++ b/src/Compilers/CSharp/Portable/BoundTree/UnboundLambda.cs
@@ -253,8 +253,8 @@ private static TypeWithAnnotations CalculateReturnType(
var bestType = returns[0].expr.GetTypeOrFunctionType();
if (bestType is FunctionTypeSymbol functionType)
{
- inferredFromFunctionType = true;
bestType = functionType.GetInternalDelegateType();
+ inferredFromFunctionType = bestType is { };
}
else
{
@@ -390,7 +390,7 @@ public static UnboundLambda Create(
Debug.Assert(syntax.IsAnonymousFunction());
bool hasErrors = !types.IsDefault && types.Any(t => t.Type?.Kind == SymbolKind.ErrorType);
- var functionType = FunctionTypeSymbol.Lazy.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => ((UnboundLambda)expr).Data.InferDelegateType());
+ var functionType = FunctionTypeSymbol.CreateIfFeatureEnabled(syntax, binder, static (binder, expr) => ((UnboundLambda)expr).Data.InferDelegateType());
var data = new PlainUnboundLambdaState(binder, returnRefKind, returnType, parameterAttributes, names, discardsOpt, types, refKinds, isAsync, isStatic, includeCache: true);
var lambda = new UnboundLambda(syntax, data, functionType, withDependencies, hasErrors: hasErrors);
data.SetUnboundLambda(lambda);
@@ -398,7 +398,7 @@ public static UnboundLambda Create(
return lambda;
}
- private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol.Lazy? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, bool hasErrors) :
+ private UnboundLambda(SyntaxNode syntax, UnboundLambdaState state, FunctionTypeSymbol? functionType, bool withDependencies, NullableWalker.VariableState? nullableState, bool hasErrors) :
this(syntax, state, functionType, withDependencies, hasErrors)
{
this._nullableState = nullableState;
diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
index 04818f305e709..b8bf3ced13628 100644
--- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
+++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
@@ -140,23 +140,11 @@ internal Conversions Conversions
private ImmutableHashSet? _usageOfUsingsRecordedInTrees = ImmutableHashSet.Empty;
///
- /// Nullable analysis data for methods, parameter default values, and attributes.
- /// The key is a symbol for methods or parameters, and syntax for attributes.
- /// The data is collected during testing only.
+ /// Optional data collected during testing only.
+ /// Used for instance for nullable analysis ()
+ /// and inferred delegate types ().
///
- internal NullableData? NullableAnalysisData;
-
- internal sealed class NullableData
- {
- internal readonly int MaxRecursionDepth;
- internal readonly ConcurrentDictionary
public abstract string Language { get; }
- public abstract Task GetBlockStructureAsync(Document document, CancellationToken cancellationToken);
+ public abstract Task GetBlockStructureAsync(Document document, BlockStructureOptions options, CancellationToken cancellationToken);
}
}
diff --git a/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs b/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs
index 7543d0317b84b..68d58fa6d0a96 100644
--- a/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs
+++ b/src/Features/Core/Portable/Structure/BlockStructureServiceWithProviders.cs
@@ -47,9 +47,12 @@ private ImmutableArray GetImportedProviders()
public override async Task GetBlockStructureAsync(
Document document,
+ BlockStructureOptions options,
CancellationToken cancellationToken)
{
- var context = await CreateContextAsync(document, cancellationToken).ConfigureAwait(false);
+ var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
+ var context = CreateContext(syntaxTree, options, cancellationToken);
+
return GetBlockStructure(context, _providers);
}
@@ -62,13 +65,6 @@ public BlockStructure GetBlockStructure(
return GetBlockStructure(context, _providers);
}
- private static async Task CreateContextAsync(Document document, CancellationToken cancellationToken)
- {
- var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false);
- var options = BlockStructureOptions.From(document.Project);
- return CreateContext(syntaxTree, options, cancellationToken);
- }
-
private static BlockStructureContext CreateContext(
SyntaxTree syntaxTree,
in BlockStructureOptions options,
diff --git a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs
index 9b3cfad98143f..c35f190587167 100644
--- a/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs
+++ b/src/Features/LanguageServer/Protocol/Handler/FoldingRanges/FoldingRangesHandler.cs
@@ -44,7 +44,8 @@ public FoldingRangesHandler()
return Array.Empty();
}
- var blockStructure = await blockStructureService.GetBlockStructureAsync(document, cancellationToken).ConfigureAwait(false);
+ var options = BlockStructureOptions.From(document.Project);
+ var blockStructure = await blockStructureService.GetBlockStructureAsync(document, options, cancellationToken).ConfigureAwait(false);
if (blockStructure == null)
{
return Array.Empty();
diff --git a/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb b/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb
index 4570e4732a071..4d4081405d9db 100644
--- a/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb
+++ b/src/Features/VisualBasic/Portable/ImplementInterface/VisualBasicImplementInterfaceCodeFixProvider.vb
@@ -7,6 +7,7 @@ Imports System.Composition
Imports System.Diagnostics.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.ImplementInterface
+Imports Microsoft.CodeAnalysis.ImplementType
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.ImplementInterface
@@ -57,9 +58,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ImplementInterface
Return
End If
+ Dim options = ImplementTypeOptions.From(document.Project)
Dim service = document.GetLanguageService(Of IImplementInterfaceService)()
Dim actions = service.GetCodeActions(
document,
+ options,
Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False),
typeNode,
cancellationToken)
diff --git a/src/Tools/ExternalAccess/FSharp/Internal/Structure/FSharpBlockStructureService.cs b/src/Tools/ExternalAccess/FSharp/Internal/Structure/FSharpBlockStructureService.cs
index 588bc7fb239ef..5826b8fdda73d 100644
--- a/src/Tools/ExternalAccess/FSharp/Internal/Structure/FSharpBlockStructureService.cs
+++ b/src/Tools/ExternalAccess/FSharp/Internal/Structure/FSharpBlockStructureService.cs
@@ -29,7 +29,7 @@ public FSharpBlockStructureService(IFSharpBlockStructureService service)
public override string Language => LanguageNames.FSharp;
- public override async Task GetBlockStructureAsync(Document document, CancellationToken cancellationToken)
+ public override async Task GetBlockStructureAsync(Document document, BlockStructureOptions options, CancellationToken cancellationToken)
{
var blockStructure = await _service.GetBlockStructureAsync(document, cancellationToken).ConfigureAwait(false);
if (blockStructure != null)
diff --git a/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionOptions.cs b/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionOptions.cs
new file mode 100644
index 0000000000000..ff99d9b65b090
--- /dev/null
+++ b/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionOptions.cs
@@ -0,0 +1,15 @@
+// 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 Microsoft.CodeAnalysis.Completion;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Completion
+{
+ internal readonly record struct OmniSharpCompletionOptions(
+ bool ShowItemsFromUnimportedNamespaces)
+ {
+ internal CompletionOptions ToCompletionOptions()
+ => CompletionOptions.Default with { ShowItemsFromUnimportedNamespaces = ShowItemsFromUnimportedNamespaces };
+ }
+}
diff --git a/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionService.cs b/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionService.cs
index 7413e21d5d498..fb5c4d40c432b 100644
--- a/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionService.cs
+++ b/src/Tools/ExternalAccess/OmniSharp/Completion/OmniSharpCompletionService.cs
@@ -7,24 +7,35 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
-using Microsoft.CodeAnalysis.Options;
+using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Completion
{
internal static class OmniSharpCompletionService
{
- public static Task<(CompletionList? completionList, bool expandItemsAvailable)> GetCompletionsAsync(
+ public static async ValueTask ShouldTriggerCompletionAsync(
this CompletionService completionService,
Document document,
int caretPosition,
CompletionTrigger trigger,
ImmutableHashSet? roles,
+ OmniSharpCompletionOptions options,
CancellationToken cancellationToken)
- => completionService.GetCompletionsInternalAsync(document, caretPosition, CompletionOptions.Default, trigger, roles, cancellationToken);
+ {
+ var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
+ return completionService.ShouldTriggerCompletion(document.Project, document.Project.LanguageServices, text, caretPosition, trigger, options.ToCompletionOptions(), roles);
+ }
- public static string? GetProviderName(this CompletionItem completionItem) => completionItem.ProviderName;
+ public static Task<(CompletionList? completionList, bool expandItemsAvailable)> GetCompletionsAsync(
+ this CompletionService completionService,
+ Document document,
+ int caretPosition,
+ CompletionTrigger trigger,
+ ImmutableHashSet? roles,
+ OmniSharpCompletionOptions options,
+ CancellationToken cancellationToken)
+ => completionService.GetCompletionsInternalAsync(document, caretPosition, options.ToCompletionOptions(), trigger, roles, cancellationToken);
- public static bool? IncludeItemsFromUnimportedNamespaces(Document document)
- => document.Project.Solution.Options.GetOption(CompletionOptions.Metadata.ShowItemsFromUnimportedNamespaces, document.Project.Language);
+ public static string? GetProviderName(this CompletionItem completionItem) => completionItem.ProviderName;
}
}
diff --git a/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs b/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs
index 121e0f7f342ec..6a7c48179da23 100644
--- a/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs
+++ b/src/Tools/ExternalAccess/OmniSharp/ImplementType/OmniSharpImplementTypeOptions.cs
@@ -10,16 +10,16 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.ImplementType
internal static class OmniSharpImplementTypeOptions
{
public static OmniSharpImplementTypeInsertionBehavior GetInsertionBehavior(OptionSet options, string language)
- => (OmniSharpImplementTypeInsertionBehavior)options.GetOption(ImplementTypeOptions.InsertionBehavior, language);
+ => (OmniSharpImplementTypeInsertionBehavior)options.GetOption(ImplementTypeOptions.Metadata.InsertionBehavior, language);
public static OptionSet SetInsertionBehavior(OptionSet options, string language, OmniSharpImplementTypeInsertionBehavior value)
- => options.WithChangedOption(ImplementTypeOptions.InsertionBehavior, language, (ImplementTypeInsertionBehavior)value);
+ => options.WithChangedOption(ImplementTypeOptions.Metadata.InsertionBehavior, language, (ImplementTypeInsertionBehavior)value);
public static OmniSharpImplementTypePropertyGenerationBehavior GetPropertyGenerationBehavior(OptionSet options, string language)
- => (OmniSharpImplementTypePropertyGenerationBehavior)options.GetOption(ImplementTypeOptions.PropertyGenerationBehavior, language);
+ => (OmniSharpImplementTypePropertyGenerationBehavior)options.GetOption(ImplementTypeOptions.Metadata.PropertyGenerationBehavior, language);
public static OptionSet SetPropertyGenerationBehavior(OptionSet options, string language, OmniSharpImplementTypePropertyGenerationBehavior value)
- => options.WithChangedOption(ImplementTypeOptions.PropertyGenerationBehavior, language, (ImplementTypePropertyGenerationBehavior)value);
+ => options.WithChangedOption(ImplementTypeOptions.Metadata.PropertyGenerationBehavior, language, (ImplementTypePropertyGenerationBehavior)value);
}
internal enum OmniSharpImplementTypeInsertionBehavior
diff --git a/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenameOptions.cs b/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenameOptions.cs
new file mode 100644
index 0000000000000..647202d6d5bdc
--- /dev/null
+++ b/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenameOptions.cs
@@ -0,0 +1,21 @@
+// 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 Microsoft.CodeAnalysis.Rename;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp
+{
+ internal readonly record struct OmniSharpRenameOptions(
+ bool RenameInComments,
+ bool RenameInStrings,
+ bool RenameOverloads)
+ {
+ internal RenameOptionSet ToRenameOptions()
+ => new(
+ RenameOverloads: RenameOverloads,
+ RenameInStrings: RenameInStrings,
+ RenameInComments: RenameInComments,
+ RenameFile: false);
+ }
+}
diff --git a/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs b/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs
new file mode 100644
index 0000000000000..d62aced448ddb
--- /dev/null
+++ b/src/Tools/ExternalAccess/OmniSharp/Rename/OmniSharpRenamer.cs
@@ -0,0 +1,24 @@
+// 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;
+using System.Collections.Immutable;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis.Rename;
+
+namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp
+{
+ internal static class OmniSharpRenamer
+ {
+ public static Task RenameSymbolAsync(
+ Solution solution,
+ ISymbol symbol,
+ string newName,
+ OmniSharpRenameOptions options,
+ ImmutableHashSet? nonConflictSymbols,
+ CancellationToken cancellationToken)
+ => Renamer.RenameSymbolAsync(solution, symbol, newName, options.ToRenameOptions(), nonConflictSymbols, cancellationToken);
+ }
+}
diff --git a/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs b/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs
index 3a85176e96456..52b0b27612bd5 100644
--- a/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs
+++ b/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureOptions.cs
@@ -3,17 +3,19 @@
// See the LICENSE file in the project root for more information.
using System;
-using System.Collections.Generic;
-using System.Text;
-using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Structure;
namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Structure
{
- internal static class OmniSharpBlockStructureOptions
+ internal readonly record struct OmniSharpBlockStructureOptions(
+ bool ShowBlockStructureGuidesForCommentsAndPreprocessorRegions,
+ bool ShowOutliningForCommentsAndPreprocessorRegions)
{
- public static readonly PerLanguageOption ShowBlockStructureGuidesForCommentsAndPreprocessorRegions = (PerLanguageOption)BlockStructureOptions.Metadata.ShowBlockStructureGuidesForCommentsAndPreprocessorRegions;
-
- public static readonly PerLanguageOption ShowOutliningForCommentsAndPreprocessorRegions = (PerLanguageOption)BlockStructureOptions.Metadata.ShowOutliningForCommentsAndPreprocessorRegions;
+ internal BlockStructureOptions ToBlockStructureOptions()
+ => BlockStructureOptions.Default with
+ {
+ ShowBlockStructureGuidesForCommentsAndPreprocessorRegions = ShowBlockStructureGuidesForCommentsAndPreprocessorRegions,
+ ShowOutliningForCommentsAndPreprocessorRegions = ShowOutliningForCommentsAndPreprocessorRegions,
+ };
}
}
diff --git a/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureService.cs b/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureService.cs
index 7b56f4a38cf46..8b353d48bbda0 100644
--- a/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureService.cs
+++ b/src/Tools/ExternalAccess/OmniSharp/Structure/OmniSharpBlockStructureService.cs
@@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
-#nullable disable
-
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Structure;
@@ -13,10 +11,10 @@ namespace Microsoft.CodeAnalysis.ExternalAccess.OmniSharp.Structure
{
internal static class OmniSharpBlockStructureService
{
- public static async Task GetBlockStructureAsync(Document document, CancellationToken cancellationToken)
+ public static async Task GetBlockStructureAsync(Document document, OmniSharpBlockStructureOptions options, CancellationToken cancellationToken)
{
var service = document.GetRequiredLanguageService();
- var blockStructure = await service.GetBlockStructureAsync(document, cancellationToken).ConfigureAwait(false);
+ var blockStructure = await service.GetBlockStructureAsync(document, options.ToBlockStructureOptions(), cancellationToken).ConfigureAwait(false);
if (blockStructure != null)
{
return new OmniSharpBlockStructure(blockStructure.Spans.SelectAsArray(x => new OmniSharpBlockSpan(x.Type, x.IsCollapsible, x.TextSpan, x.HintSpan, x.BannerText, x.AutoCollapse, x.IsDefaultCollapsed)));
diff --git a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs b/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs
index 63fc07b6e2c3e..463a02e623599 100644
--- a/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs
+++ b/src/Tools/ExternalAccess/Razor/RazorDocumentServiceProviderWrapper.cs
@@ -1,27 +1,26 @@
// 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 annotations
using System;
+using System.Runtime.CompilerServices;
using Microsoft.CodeAnalysis.Host;
+using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.ExternalAccess.Razor
{
internal sealed class RazorDocumentServiceProviderWrapper : IDocumentServiceProvider, IDocumentOperationService
{
private readonly IRazorDocumentServiceProvider _innerDocumentServiceProvider;
- private readonly object _lock;
- private RazorSpanMappingServiceWrapper? _spanMappingService;
- private RazorDocumentExcerptServiceWrapper? _excerptService;
- private RazorDocumentPropertiesServiceWrapper? _documentPropertiesService;
+ // The lazily initialized service fields use StrongBox to explicitly allow null as an initialized value.
+ private StrongBox? _lazySpanMappingService;
+ private StrongBox? _lazyExcerptService;
+ private StrongBox? _lazyDocumentPropertiesService;
public RazorDocumentServiceProviderWrapper(IRazorDocumentServiceProvider innerDocumentServiceProvider)
{
_innerDocumentServiceProvider = innerDocumentServiceProvider ?? throw new ArgumentNullException(nameof(innerDocumentServiceProvider));
-
- _lock = new object();
}
public bool CanApplyChange => _innerDocumentServiceProvider.CanApplyChange;
@@ -33,75 +32,44 @@ public RazorDocumentServiceProviderWrapper(IRazorDocumentServiceProvider innerDo
var serviceType = typeof(TService);
if (serviceType == typeof(ISpanMappingService))
{
- if (_spanMappingService == null)
- {
- lock (_lock)
+ var spanMappingService = LazyInitialization.EnsureInitialized(
+ ref _lazySpanMappingService,
+ static documentServiceProvider =>
{
- if (_spanMappingService == null)
- {
- var razorMappingService = _innerDocumentServiceProvider.GetService();
- if (razorMappingService != null)
- {
- _spanMappingService = new RazorSpanMappingServiceWrapper(razorMappingService);
- }
- else
- {
- return this as TService;
- }
- }
- }
- }
+ var razorMappingService = documentServiceProvider.GetService();
+ return razorMappingService != null ? new RazorSpanMappingServiceWrapper(razorMappingService) : null;
+ },
+ _innerDocumentServiceProvider);
- return (TService)(object)_spanMappingService;
+ return (TService?)spanMappingService;
}
if (serviceType == typeof(IDocumentExcerptService))
{
- if (_excerptService == null)
- {
- lock (_lock)
+ var excerptService = LazyInitialization.EnsureInitialized(
+ ref _lazyExcerptService,
+ static documentServiceProvider =>
{
- if (_excerptService == null)
- {
- var excerptService = _innerDocumentServiceProvider.GetService();
- if (excerptService != null)
- {
- _excerptService = new RazorDocumentExcerptServiceWrapper(excerptService);
- }
- else
- {
- return this as TService;
- }
- }
- }
- }
+ var razorExcerptService = documentServiceProvider.GetService();
+ return razorExcerptService is not null ? new RazorDocumentExcerptServiceWrapper(razorExcerptService) : null;
+ },
+ _innerDocumentServiceProvider);
- return (TService)(object)_excerptService;
+ return (TService?)excerptService;
}
if (serviceType == typeof(DocumentPropertiesService))
{
- if (_documentPropertiesService == null)
- {
- lock (_lock)
+ var documentPropertiesService = LazyInitialization.EnsureInitialized(
+ ref _lazyDocumentPropertiesService,
+ static documentServiceProvider =>
{
- if (_documentPropertiesService == null)
- {
- var documentPropertiesService = _innerDocumentServiceProvider.GetService();
-
- if (documentPropertiesService != null)
- {
- _documentPropertiesService = new RazorDocumentPropertiesServiceWrapper(documentPropertiesService);
- }
- else
- {
- return this as TService;
- }
- }
- }
- }
+ var razorDocumentPropertiesService = documentServiceProvider.GetService();
+ return razorDocumentPropertiesService is not null ? new RazorDocumentPropertiesServiceWrapper(razorDocumentPropertiesService) : null;
+ },
+ _innerDocumentServiceProvider);
- return (TService)(object)_documentPropertiesService;
+ return (TService?)(object?)documentPropertiesService;
}
return this as TService;
diff --git a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs
index 039e54099fa28..f71c20f945332 100644
--- a/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs
+++ b/src/VisualStudio/CSharp/Impl/Options/AdvancedOptionPageControl.xaml.cs
@@ -117,11 +117,11 @@ public AdvancedOptionPageControl(OptionStore optionStore, IComponentModel compon
BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.DontPutOutOrRefOnStruct, LanguageNames.CSharp);
- BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp);
- BindToOption(at_the_end, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.CSharp);
+ BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.CSharp);
+ BindToOption(at_the_end, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.CSharp);
- BindToOption(prefer_throwing_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.CSharp);
- BindToOption(prefer_auto_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.CSharp);
+ BindToOption(prefer_throwing_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.CSharp);
+ BindToOption(prefer_auto_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.CSharp);
BindToOption(Report_invalid_placeholders_in_string_dot_format_calls, ValidateFormatStringOption.ReportInvalidPlaceholdersInStringDotFormatCalls, LanguageNames.CSharp);
diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioActiveDocumentTracker.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioActiveDocumentTracker.cs
index e97deba28cdcd..79f8cb1fcf26b 100644
--- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioActiveDocumentTracker.cs
+++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioActiveDocumentTracker.cs
@@ -147,10 +147,19 @@ public void TrackNewActiveWindowFrame(IVsWindowFrame frame)
_activeFrame = frame;
- if (!_visibleFrames.Any(f => f.Frame == frame))
+ var existingFrame = _visibleFrames.FirstOrDefault(f => f.Frame == frame);
+ if (existingFrame == null)
{
_visibleFrames = _visibleFrames.Add(new FrameListener(this, frame));
}
+ else if (existingFrame.TextBuffer == null)
+ {
+ // If no text buffer is associated with existing frame, remove the existing frame and add the new one.
+ // Note that we do not need to disconnect the existing frame here. It will get disconnected along with
+ // the new frame whenever the document is closed or de-activated.
+ _visibleFrames = _visibleFrames.Remove(existingFrame);
+ _visibleFrames = _visibleFrames.Add(new FrameListener(this, frame));
+ }
this.DocumentsChanged?.Invoke(this, EventArgs.Empty);
}
@@ -210,7 +219,7 @@ private class FrameListener : IVsWindowFrameNotify, IVsWindowFrameNotify2
private readonly VisualStudioActiveDocumentTracker _documentTracker;
private readonly uint _frameEventsCookie;
- private readonly ITextBuffer? _textBuffer;
+ internal ITextBuffer? TextBuffer { get; }
public FrameListener(VisualStudioActiveDocumentTracker service, IVsWindowFrame frame)
{
@@ -225,11 +234,11 @@ public FrameListener(VisualStudioActiveDocumentTracker service, IVsWindowFrame f
{
if (docData is IVsTextBuffer bufferAdapter)
{
- _textBuffer = _documentTracker._editorAdaptersFactoryService.GetDocumentBuffer(bufferAdapter);
+ TextBuffer = _documentTracker._editorAdaptersFactoryService.GetDocumentBuffer(bufferAdapter);
- if (_textBuffer != null && !_textBuffer.ContentType.IsOfType(ContentTypeNames.RoslynContentType))
+ if (TextBuffer != null && !TextBuffer.ContentType.IsOfType(ContentTypeNames.RoslynContentType))
{
- _textBuffer.Changed += NonRoslynTextBuffer_Changed;
+ TextBuffer.Changed += NonRoslynTextBuffer_Changed;
}
}
}
@@ -244,12 +253,12 @@ private void NonRoslynTextBuffer_Changed(object sender, TextContentChangedEventA
///
public DocumentId? GetDocumentId(Workspace workspace)
{
- if (_textBuffer == null)
+ if (TextBuffer == null)
{
return null;
}
- var textContainer = _textBuffer.AsTextContainer();
+ var textContainer = TextBuffer.AsTextContainer();
return workspace.GetDocumentIdInCurrentContext(textContainer);
}
@@ -283,9 +292,9 @@ private int Disconnect()
_documentTracker.AssertIsForeground();
_documentTracker.RemoveFrame(this);
- if (_textBuffer != null)
+ if (TextBuffer != null)
{
- _textBuffer.Changed -= NonRoslynTextBuffer_Changed;
+ TextBuffer.Changed -= NonRoslynTextBuffer_Changed;
}
if (_frameEventsCookie != VSConstants.VSCOOKIE_NIL)
diff --git a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb
index 26b84d62be62b..9855802da5742 100644
--- a/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb
+++ b/src/VisualStudio/VisualBasic/Impl/Options/AdvancedOptionPageControl.xaml.vb
@@ -133,11 +133,11 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.Options
BindToOption(DontPutOutOrRefOnStruct, ExtractMethodOptions.DontPutOutOrRefOnStruct, LanguageNames.VisualBasic)
' Implement Interface or Abstract Class
- BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic)
- BindToOption(at_the_end, ImplementTypeOptions.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.VisualBasic)
+ BindToOption(with_other_members_of_the_same_kind, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.WithOtherMembersOfTheSameKind, LanguageNames.VisualBasic)
+ BindToOption(at_the_end, ImplementTypeOptions.Metadata.InsertionBehavior, ImplementTypeInsertionBehavior.AtTheEnd, LanguageNames.VisualBasic)
- BindToOption(prefer_throwing_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.VisualBasic)
- BindToOption(prefer_auto_properties, ImplementTypeOptions.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.VisualBasic)
+ BindToOption(prefer_throwing_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferThrowingProperties, LanguageNames.VisualBasic)
+ BindToOption(prefer_auto_properties, ImplementTypeOptions.Metadata.PropertyGenerationBehavior, ImplementTypePropertyGenerationBehavior.PreferAutoProperties, LanguageNames.VisualBasic)
' Inline hints
BindToOption(DisplayAllHintsWhilePressingAltF1, InlineHintsViewOptions.DisplayAllHintsWhilePressingAltF1)
diff --git a/src/Workspaces/Core/Portable/Rename/RenameOptions.cs b/src/Workspaces/Core/Portable/Rename/RenameOptions.cs
index cac76215e79ac..8d4cfa0164ebe 100644
--- a/src/Workspaces/Core/Portable/Rename/RenameOptions.cs
+++ b/src/Workspaces/Core/Portable/Rename/RenameOptions.cs
@@ -22,21 +22,12 @@ public static class RenameOptions
public static Option PreviewChanges { get; } = new Option(nameof(RenameOptions), nameof(PreviewChanges), defaultValue: false);
}
- internal struct RenameOptionSet
+ internal readonly record struct RenameOptionSet(
+ bool RenameOverloads,
+ bool RenameInStrings,
+ bool RenameInComments,
+ bool RenameFile)
{
- public readonly bool RenameOverloads;
- public readonly bool RenameInStrings;
- public readonly bool RenameInComments;
- public readonly bool RenameFile;
-
- public RenameOptionSet(bool renameOverloads, bool renameInStrings, bool renameInComments, bool renameFile)
- {
- RenameOverloads = renameOverloads;
- RenameInStrings = renameInStrings;
- RenameInComments = renameInComments;
- RenameFile = renameFile;
- }
-
internal static RenameOptionSet From(Solution solution)
=> From(solution, options: null);
diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/LazyInitialization.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/LazyInitialization.cs
index 11ff82a69439b..556f1985237e8 100644
--- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/LazyInitialization.cs
+++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/LazyInitialization.cs
@@ -4,6 +4,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
+using System.Runtime.CompilerServices;
using System.Threading;
namespace Roslyn.Utilities
@@ -22,7 +23,7 @@ internal static T InterlockedStore([NotNull] ref T? target, T value) where T
/// more than once by multiple threads, but only one of those values will successfully be written to the target.
/// The target value.
public static T EnsureInitialized([NotNull] ref T? target, Func valueFactory) where T : class
- => Volatile.Read(ref target!) ?? InterlockedStore(ref target!, valueFactory());
+ => Volatile.Read(ref target!) ?? InterlockedStore(ref target, valueFactory());
///
/// Ensure that the given target value is initialized (not null) in a thread-safe manner.
@@ -37,7 +38,41 @@ public static T EnsureInitialized([NotNull] ref T? target, Func valueFacto
public static T EnsureInitialized([NotNull] ref T? target, Func valueFactory, U state)
where T : class
{
- return Volatile.Read(ref target!) ?? InterlockedStore(ref target!, valueFactory(state));
+ return Volatile.Read(ref target!) ?? InterlockedStore(ref target, valueFactory(state));
+ }
+
+ ///
+ /// Ensure that the given target value is initialized in a thread-safe manner. This overload supports the
+ /// initialization of value types, and reference type fields where is considered an
+ /// initialized value.
+ ///
+ /// The type of the target value.
+ /// A target value box to initialize.
+ /// A factory delegate to create a new instance of the target value. Note that this delegate may be called
+ /// more than once by multiple threads, but only one of those values will successfully be written to the target.
+ /// The target value.
+ public static T? EnsureInitialized([NotNull] ref StrongBox? target, Func valueFactory)
+ {
+ var box = Volatile.Read(ref target!) ?? InterlockedStore(ref target, new StrongBox(valueFactory()));
+ return box.Value;
+ }
+
+ ///
+ /// Ensure that the given target value is initialized in a thread-safe manner. This overload supports the
+ /// initialization of value types, and reference type fields where is considered an
+ /// initialized value.
+ ///
+ /// The type of the target value.
+ /// A target value box to initialize.
+ /// The type of the argument passed to the value factory.
+ /// A factory delegate to create a new instance of the target value. Note that this delegate may be called
+ /// more than once by multiple threads, but only one of those values will successfully be written to the target.
+ /// An argument passed to the value factory.
+ /// The target value.
+ public static T? EnsureInitialized([NotNull] ref StrongBox? target, Func valueFactory, U state)
+ {
+ var box = Volatile.Read(ref target!) ?? InterlockedStore(ref target, new StrongBox(valueFactory(state)));
+ return box.Value;
}
}
}