提交 0640c762 编写于 作者: J Jason Malinowski

Add some nullable annotations in options system

There appeared to be one bug: if a document was opened that had a null
file path, there was a risk that we would crash because we would be
passing that around to the editor API in the legacy case.
上级 f52a24a0
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
......@@ -17,8 +19,8 @@ private class DocumentOptions : IDocumentOptions
{
private readonly ICodingConventionsSnapshot _codingConventionSnapshot;
private readonly IErrorLoggerService _errorLogger;
private static readonly ConditionalWeakTable<IReadOnlyDictionary<string, object>, IReadOnlyDictionary<string, string>> s_convertedDictionaryCache =
new ConditionalWeakTable<IReadOnlyDictionary<string, object>, IReadOnlyDictionary<string, string>>();
private static readonly ConditionalWeakTable<IReadOnlyDictionary<string, object?>, IReadOnlyDictionary<string, string?>> s_convertedDictionaryCache =
new ConditionalWeakTable<IReadOnlyDictionary<string, object?>, IReadOnlyDictionary<string, string?>>();
public DocumentOptions(ICodingConventionsSnapshot codingConventionSnapshot, IErrorLoggerService errorLogger)
{
......@@ -26,7 +28,7 @@ public DocumentOptions(ICodingConventionsSnapshot codingConventionSnapshot, IErr
_errorLogger = errorLogger;
}
public bool TryGetDocumentOption(OptionKey option, out object value)
public bool TryGetDocumentOption(OptionKey option, out object? value)
{
var editorConfigPersistence = option.Option.StorageLocations.OfType<IEditorConfigStorageLocation>().SingleOrDefault();
if (editorConfigPersistence == null)
......@@ -63,33 +65,33 @@ public bool TryGetDocumentOption(OptionKey option, out object value)
/// where we just convert the values to strings with ToString(). Ordering of the underlying dictionary is preserved, so that way
/// code that relies on the underlying ordering of the underlying dictionary isn't affected.
/// </summary>
private class StringConvertingDictionary : IReadOnlyDictionary<string, string>
private class StringConvertingDictionary : IReadOnlyDictionary<string, string?>
{
private readonly IReadOnlyDictionary<string, object> _underlyingDictionary;
private readonly IReadOnlyDictionary<string, object?> _underlyingDictionary;
public StringConvertingDictionary(IReadOnlyDictionary<string, object> underlyingDictionary)
public StringConvertingDictionary(IReadOnlyDictionary<string, object?> underlyingDictionary)
{
_underlyingDictionary = underlyingDictionary ?? throw new ArgumentNullException(nameof(underlyingDictionary));
}
public string this[string key] => _underlyingDictionary[key]?.ToString();
public string? this[string key] => _underlyingDictionary[key]?.ToString();
public IEnumerable<string> Keys => _underlyingDictionary.Keys;
public IEnumerable<string> Values => _underlyingDictionary.Values.Select(s => s?.ToString());
public IEnumerable<string?> Values => _underlyingDictionary.Values.Select(s => s?.ToString());
public int Count => _underlyingDictionary.Count;
public bool ContainsKey(string key) => _underlyingDictionary.ContainsKey(key);
public IEnumerator<KeyValuePair<string, string>> GetEnumerator()
public IEnumerator<KeyValuePair<string, string?>> GetEnumerator()
{
foreach (var pair in _underlyingDictionary)
{
yield return new KeyValuePair<string, string>(pair.Key, pair.Value?.ToString());
yield return new KeyValuePair<string, string?>(pair.Key, pair.Value?.ToString());
}
}
public bool TryGetValue(string key, out string value)
public bool TryGetValue(string key, out string? value)
{
if (_underlyingDictionary.TryGetValue(key, out var objectValue))
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
......@@ -36,7 +38,7 @@ internal LegacyEditorConfigDocumentOptionsProvider(Workspace workspace, ICodingC
_workspace = workspace;
_listener = listenerProvider.GetListener(FeatureAttribute.Workspace);
_codingConventionsManager = codingConventionsManager;
_errorLogger = workspace.Services.GetService<IErrorLoggerService>();
_errorLogger = workspace.Services.GetRequiredService<IErrorLoggerService>();
workspace.DocumentOpened += Workspace_DocumentOpened;
workspace.DocumentClosed += Workspace_DocumentClosed;
......@@ -72,12 +74,16 @@ private void Workspace_DocumentOpened(object sender, DocumentEventArgs e)
{
var documentId = e.Document.Id;
var filePath = e.Document.FilePath;
_openDocumentContexts.Add(documentId, Task.Run(async () =>
if (filePath != null)
{
var context = await GetConventionContextAsync(filePath, CancellationToken.None).ConfigureAwait(false);
OnCodingConventionContextCreated(documentId, context);
return context;
}));
_openDocumentContexts.Add(documentId, Task.Run(async () =>
{
var context = await GetConventionContextAsync(filePath, CancellationToken.None).ConfigureAwait(false);
OnCodingConventionContextCreated(documentId, context);
return context;
}));
}
}
}
......@@ -99,7 +105,7 @@ private void Workspace_WorkspaceChanged(object sender, WorkspaceChangeEventArgs
}
}
private void ClearOpenFileCache(ProjectId projectId = null)
private void ClearOpenFileCache(ProjectId? projectId = null)
{
lock (_gate)
{
......@@ -133,7 +139,7 @@ private void ReleaseContext_NoLock(Task<ICodingConventionContext> contextTask)
TaskScheduler.Default);
}
public async Task<IDocumentOptions> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken)
public async Task<IDocumentOptions?> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken)
{
Task<ICodingConventionContext> contextTask;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Composition;
using System.IO;
......@@ -32,7 +34,7 @@ class LegacyEditorConfigDocumentOptionsProviderFactory : IDocumentOptionsProvide
_asynchronousOperationListenerProvider = asynchronousOperationListenerProvider;
}
public IDocumentOptionsProvider TryCreate(Workspace workspace)
public IDocumentOptionsProvider? TryCreate(Workspace workspace)
{
if (EditorConfigDocumentOptionsProviderFactory.ShouldUseNativeEditorConfigSupport(workspace))
{
......@@ -84,7 +86,7 @@ private Task OnConventionFileChanged(object sender, ConventionsFileChangeEventAr
return ConventionFileChanged?.Invoke(this, arg) ?? Task.CompletedTask;
}
public event ConventionsFileChangedAsyncEventHandler ConventionFileChanged;
public event ConventionsFileChangedAsyncEventHandler? ConventionFileChanged;
public event ContextFileMovedAsyncEventHandler ContextFileMoved
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections.Immutable;
using System.Composition;
......@@ -18,7 +20,7 @@ public EditorConfigDocumentOptionsProviderFactory()
{
}
public IDocumentOptionsProvider TryCreate(Workspace workspace)
public IDocumentOptionsProvider? TryCreate(Workspace workspace)
{
if (!ShouldUseNativeEditorConfigSupport(workspace))
{
......@@ -26,7 +28,7 @@ public IDocumentOptionsProvider TryCreate(Workspace workspace)
return null;
}
return new EditorConfigDocumentOptionsProvider(workspace.Services.GetService<IErrorLoggerService>());
return new EditorConfigDocumentOptionsProvider(workspace.Services.GetRequiredService<IErrorLoggerService>());
}
private const string LocalRegistryPath = @"Roslyn\Internal\OnOff\Features\";
......@@ -49,7 +51,7 @@ public EditorConfigDocumentOptionsProvider(IErrorLoggerService errorLogger)
_errorLogger = errorLogger;
}
public async Task<IDocumentOptions> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken)
public async Task<IDocumentOptions?> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken)
{
var options = await document.GetAnalyzerOptionsAsync(cancellationToken).ConfigureAwait(false);
......@@ -67,7 +69,7 @@ public DocumentOptions(ImmutableDictionary<string, string> options, IErrorLogger
_errorLogger = errorLogger;
}
public bool TryGetDocumentOption(OptionKey option, out object value)
public bool TryGetDocumentOption(OptionKey option, out object? value)
{
var editorConfigPersistence = option.Option.StorageLocations.OfType<IEditorConfigStorageLocation>().SingleOrDefault();
if (editorConfigPersistence == null)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
namespace Microsoft.CodeAnalysis.Options
{
/// <summary>
......@@ -7,6 +9,12 @@ namespace Microsoft.CodeAnalysis.Options
/// </summary>
interface IDocumentOptions
{
bool TryGetDocumentOption(OptionKey option, out object value);
/// <summary>
/// Attempts to fetch the value for the given option.
/// </summary>
/// <param name="option"></param>
/// <param name="value">The value returned. May be null even if the function returns true as "null" may be valid value for some options.</param>
/// <returns>True if this provider had a specific value for this option. False to indicate other providers should be queried.</returns>
bool TryGetDocumentOption(OptionKey option, out object? value);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
......@@ -22,6 +26,6 @@ interface IDocumentOptionsProvider
/// Fetches a <see cref="IDocumentOptions"/> for the given document. Any asynchronous work (looking for config files, etc.)
/// should be done here. Can return a null-valued task to mean there is no options being provided for this document.
/// </summary>
Task<IDocumentOptions> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken);
Task<IDocumentOptions?> GetOptionsForDocumentAsync(Document document, CancellationToken cancellationToken);
}
}
namespace Microsoft.CodeAnalysis.Options
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
namespace Microsoft.CodeAnalysis.Options
{
/// <remarks>
/// This interface exists so the Visual Studio workspace can create the .editorconfig provider,
......@@ -8,6 +12,6 @@
/// </remarks>
interface IDocumentOptionsProviderFactory
{
IDocumentOptionsProvider TryCreate(Workspace workspace);
IDocumentOptionsProvider? TryCreate(Workspace workspace);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
......@@ -166,14 +168,14 @@ private class DocumentSpecificOptionSet : OptionSet
{
private readonly OptionSet _underlyingOptions;
private readonly List<IDocumentOptions> _documentOptions;
private ImmutableDictionary<OptionKey, object> _values;
private ImmutableDictionary<OptionKey, object?> _values;
public DocumentSpecificOptionSet(List<IDocumentOptions> documentOptions, OptionSet underlyingOptions)
: this(documentOptions, underlyingOptions, ImmutableDictionary<OptionKey, object>.Empty)
: this(documentOptions, underlyingOptions, ImmutableDictionary<OptionKey, object?>.Empty)
{
}
public DocumentSpecificOptionSet(List<IDocumentOptions> documentOptions, OptionSet underlyingOptions, ImmutableDictionary<OptionKey, object> values)
public DocumentSpecificOptionSet(List<IDocumentOptions> documentOptions, OptionSet underlyingOptions, ImmutableDictionary<OptionKey, object?> values)
{
_documentOptions = documentOptions;
_underlyingOptions = underlyingOptions;
......@@ -181,7 +183,7 @@ public DocumentSpecificOptionSet(List<IDocumentOptions> documentOptions, OptionS
}
[PerformanceSensitive("https://github.com/dotnet/roslyn/issues/30819", AllowLocks = false)]
public override object GetOption(OptionKey optionKey)
public override object? GetOption(OptionKey optionKey)
{
// If we already know the document specific value, we're done
if (_values.TryGetValue(optionKey, out var value))
......@@ -202,7 +204,7 @@ public override object GetOption(OptionKey optionKey)
return _underlyingOptions.GetOption(optionKey);
}
public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object value)
public override OptionSet WithChangedOption(OptionKey optionAndLanguage, object? value)
{
return new DocumentSpecificOptionSet(_documentOptions, _underlyingOptions, _values.Add(optionAndLanguage, value));
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册