提交 05b2ed1b 编写于 作者: C Cyrus Najmabadi

Merge remote-tracking branch 'upstream/master' into allowSymbolOOPAndNoSymAndProjId

## API Review Notes for September 30th, 2019
### Changes reviewed
Starting commit: `38c90f8401f9e3ee5fb7c82aac36f6b85fdda979`
Ending Commit: `b8a5611e3db4f7ac3b5e1404b129304b5baf843e`
### Notes
IVariableDeclarationOperation:
- What does scripting do for using local declarations in IOperation?
......
## API Review Notes for April 15th, 2020
### Changes reviewed
Starting commit: `c827247a767c0c434dcb77496038b163b9e011da`
Ending Commit: `3375a7b209d1f590940af043e9e39f2cbb503845`
### Notes
#### Everything but Source Generators:
* `Project.RemoveDocuments`: Everything else in this type uses `IEnumerable<T>`, not `ImmutableArray<T>`. If we could start again from day 1 we might make everything that, but given that everything today uses the former, we should do so here. This applies to the overloads on `Solution` as well.
* `Solution.RemoveAdditonalDocuments/RemoveAnalyzerConfigDocuments`: We added these to `Solution`, but not to project, even though that also has the invididual `Remove` methods. We should add them to `Project` as well.
* `DocumentOptionSet.GetOption`: This API was added as a `new` on the concrete `DocumentOptionSet` to cover a change when we moved the implementation up to `OptionSet` and made the method non-virtual. This is not actually a breaking change, so we should remove this.
#### Source Generators:
As a general note, changes that we make here are _not_ considered breaking changes post 16.6.
However, we did take a broad overview of the API surface and have some notes for @chsienki.
* `GeneratorAttribute`: we should consider adding a `LanguageName` attribute to this type.
* `InitializationContext`: Rename to something like `SourceGeneratorInitialzationContext` or similar. Current name is ambiguous.
* `InitializationContext.RegisterForNotifications`:
* Naming is inconsistent with existing `Action` ending for analyzer register methods.
* `SyntaxReceiverCreator`: all other `RegisterForXAction` methods in analyzers use an `Action` or `Func`, as appropriate. We should consider doing the same here.
* `ISyntaxReceiver`: This may be redundant with existing APIs.
* `SourceGeneratorContext`: There is not currently a way to share semantic models and other binding info between source generators. We should design a way for this info to be shared so we can avoid introducing lots of binding from generators.
ErrorLogPath removed from CLO, moved to ErrorLogOptions
- We should probably maintain this API
- Just forward to the old implementation
TextDocument
- Should we not remove the constructor? This is a binary breaking change
- This empty constructor cannot be used.
- We'll keep the change.
Workspace
- CanApplyParseOptionChange going from virtual protected to virtual public is a source and binary breaking change
- We should look at having a new non-virtual public method on Workspace and forward to this API.
SyntaxFactory.AnonymousMethodExpression
- Added overload with multiple parameters
- Followup on usage
Formatter.OrganizeUsings
- Should make cancellationtoken optional?
- Need to followup with framework on what the current guidelines are
- Make this default for now
......@@ -35,9 +35,9 @@ internal CSharpCommandLineParser(bool isScriptCommandLineParser = false)
protected override string RegularFileExtension { get { return ".cs"; } }
protected override string ScriptFileExtension { get { return ".csx"; } }
internal sealed override CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectoryOpt, string additionalReferenceDirectories)
internal sealed override CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories)
{
return Parse(args, baseDirectory, sdkDirectoryOpt, additionalReferenceDirectories);
return Parse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories);
}
/// <summary>
......
......@@ -67,7 +67,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi
return Directory.EnumerateFiles(directory, fileNamePattern, searchOption);
}
internal abstract CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectoryOpt, string additionalReferenceDirectories);
internal abstract CommandLineArguments CommonParse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories);
/// <summary>
/// Parses a command line.
......@@ -77,7 +77,7 @@ internal virtual IEnumerable<string> EnumerateFiles(string? directory, string fi
/// <param name="sdkDirectory">The directory to search for mscorlib, or null if not available.</param>
/// <param name="additionalReferenceDirectories">A string representing additional reference paths.</param>
/// <returns>a <see cref="CommandLineArguments"/> object representing the parsed command line.</returns>
public CommandLineArguments Parse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string additionalReferenceDirectories)
public CommandLineArguments Parse(IEnumerable<string> args, string baseDirectory, string? sdkDirectory, string? additionalReferenceDirectories)
{
return CommonParse(args, baseDirectory, sdkDirectory, additionalReferenceDirectories);
}
......
......@@ -101,9 +101,8 @@ private async Task<DefinitionItem> GetDefinitionItemAsync(ISymbol definition)
{
if (!_definitionToItem.TryGetValue(definition, out var definitionItem))
{
var projectId = _solution.GetExactProjectId(definition);
definitionItem = await definition.ToClassifiedDefinitionItemAsync(
_solution.GetProject(projectId), includeHiddenLocations: false,
_solution.GetOriginatingProject(definition), includeHiddenLocations: false,
_options, _context.CancellationToken).ConfigureAwait(false);
_definitionToItem[definition] = definitionItem;
......
......@@ -61,18 +61,14 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym
var (symbol, project) = symbolAndProjectOpt.Value;
return await FindSourceImplementationsAsync(
symbol, project.Solution, cancellationToken).ConfigureAwait(false);
project.Solution, symbol, cancellationToken).ConfigureAwait(false);
}
private static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISymbol> implementations, string message)?> FindSourceImplementationsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken)
Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{
var builder = new HashSet<ISymbol>(SymbolEquivalenceComparer.Instance);
// Find the direct implementations first.
builder.AddRange(await FindSourceImplementationsWorkerAsync(
symbol, solution, cancellationToken).ConfigureAwait(false));
// If we're in a linked file, try to find all the symbols this links to, and find all the implementations of
// each of those linked symbols. De-dupe the results so the user only gets unique results.
var linkedSymbols = await SymbolFinder.FindLinkedSymbolsAsync(
......@@ -81,25 +77,24 @@ public static async Task<(Solution solution, ISymbol symbol, ImmutableArray<ISym
foreach (var linkedSymbol in linkedSymbols)
{
builder.AddRange(await FindSourceImplementationsWorkerAsync(
linkedSymbol, solution, cancellationToken).ConfigureAwait((bool)false));
solution, linkedSymbol, cancellationToken).ConfigureAwait((bool)false));
}
var result = builder.ToImmutableArray();
var message = result.IsEmpty ? EditorFeaturesResources.The_symbol_has_no_implementations : null;
return result.Length == 0
? (solution, symbol, result, EditorFeaturesResources.The_symbol_has_no_implementations)
: (solution, symbol, result, null);
return (solution, symbol, result, message);
}
private static async Task<ImmutableArray<ISymbol>> FindSourceImplementationsWorkerAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken)
Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{
var implementations = await FindSourceAndMetadataImplementationsAsync(symbol, solution, cancellationToken).ConfigureAwait(false);
var implementations = await FindSourceAndMetadataImplementationsAsync(solution, symbol, cancellationToken).ConfigureAwait(false);
return implementations.WhereAsArray(s => s.Locations.Any(l => l.IsInSource));
}
private static async Task<ImmutableArray<ISymbol>> FindSourceAndMetadataImplementationsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken)
Solution solution, ISymbol symbol, CancellationToken cancellationToken)
{
if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
{
......
......@@ -47,11 +47,10 @@ public async Task FindBasesAsync(Document document, int position, IFindUsagesCon
baseSymbol, solution, cancellationToken).ConfigureAwait(false);
if (sourceDefinition != null)
{
var sourceDefProjectId = solution.GetExactProjectId(sourceDefinition);
var definitionItem = await sourceDefinition.ToClassifiedDefinitionItemAsync(
solution.GetProject(sourceDefProjectId), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken)
.ConfigureAwait(false);
solution.GetOriginatingProject(sourceDefinition), includeHiddenLocations: false,
FindReferencesSearchOptions.Default, cancellationToken: cancellationToken).ConfigureAwait(false);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
found = true;
}
......
......@@ -2,18 +2,20 @@
// 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
namespace Microsoft.CodeAnalysis.Interactive
{
internal partial class InteractiveHost
{
private struct InitializedRemoteService
private readonly struct InitializedRemoteService
{
public readonly RemoteService ServiceOpt;
public readonly RemoteService Service;
public readonly RemoteExecutionResult InitializationResult;
public InitializedRemoteService(RemoteService service, RemoteExecutionResult initializationResult)
{
ServiceOpt = service;
Service = service;
InitializationResult = initializationResult;
}
}
......
......@@ -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 System;
using System.Globalization;
using System.Threading;
......@@ -41,7 +43,7 @@ public void Dispose()
// If the value has been calculated already, dispose the service.
if (InitializedService.TryGetValue(out var initializedService))
{
initializedService.ServiceOpt?.Dispose();
initializedService.Service?.Dispose();
}
}
......@@ -101,7 +103,7 @@ private async Task<InitializedRemoteService> TryStartAndInitializeProcessAsync(C
}
}
private Task<RemoteService> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
private Task<RemoteService?> TryStartProcessAsync(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{
return Task.Run(() => Host.TryStartProcess(hostPath, culture, cancellationToken));
}
......
......@@ -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 System;
using System.Diagnostics;
using System.Runtime.Remoting;
......@@ -12,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Interactive
{
internal partial class InteractiveHost
{
internal class RemoteAsyncOperation<TResult> : MarshalByRefObject
internal sealed class RemoteAsyncOperation<TResult> : MarshalByRefObject
{
private readonly RemoteService _remoteService;
private readonly TaskCompletionSource<TResult> _completion;
......@@ -20,17 +22,12 @@ internal class RemoteAsyncOperation<TResult> : MarshalByRefObject
internal RemoteAsyncOperation(RemoteService service)
{
Debug.Assert(service != null);
_remoteService = service;
_completion = new TaskCompletionSource<TResult>();
_processExitedHandler = new EventHandler((_, __) => ProcessExited());
}
public override object InitializeLifetimeService()
{
return null;
}
public override object? InitializeLifetimeService() => null;
public Task<TResult> ExecuteAsync(Action<Service, RemoteAsyncOperation<TResult>> action)
{
......@@ -67,7 +64,7 @@ public void Completed(TResult result)
private void ProcessExited()
{
Completed(result: default(TResult));
Completed(result: default!);
}
public void SetResult(TResult result)
......
......@@ -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 System;
using System.Diagnostics;
using System.IO;
......@@ -23,17 +25,13 @@ internal sealed class RemoteService
private readonly bool _joinOutputWritingThreadsOnDisposal;
// output pumping threads (stream output from stdout/stderr of the host process to the output/errorOutput writers)
private InteractiveHost _host; // nulled on dispose
private Thread _readOutputThread; // nulled on dispose
private Thread _readErrorOutputThread; // nulled on dispose
private InteractiveHost? _host; // nulled on dispose
private Thread? _readOutputThread; // nulled on dispose
private Thread? _readErrorOutputThread; // nulled on dispose
private volatile ProcessExitHandlerStatus _processExitHandlerStatus; // set to Handled on dispose
internal RemoteService(InteractiveHost host, Process process, int processId, Service service)
{
Debug.Assert(host != null);
Debug.Assert(process != null);
Debug.Assert(service != null);
Process = process;
Service = service;
......
// 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
extern alias Scripting;
using System;
......@@ -10,7 +13,6 @@
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
......@@ -27,8 +29,6 @@
namespace Microsoft.CodeAnalysis.Interactive
{
using RelativePathResolver = Scripting::Microsoft.CodeAnalysis.RelativePathResolver;
internal partial class InteractiveHost
{
/// <summary>
......@@ -38,12 +38,9 @@ internal sealed class Service : MarshalByRefObject, IDisposable
{
private static readonly ManualResetEventSlim s_clientExited = new ManualResetEventSlim(false);
private static Control s_control;
private static Control? s_control;
private InteractiveAssemblyLoader _assemblyLoader;
private MetadataShadowCopyProvider _metadataFileProvider;
private ReplServiceProvider _replServiceProvider;
private InteractiveScriptGlobals _globals;
private ServiceState? _serviceState;
// Session is not thread-safe by itself, and the compilation
// and execution of scripts are asynchronous operations.
......@@ -52,27 +49,44 @@ internal sealed class Service : MarshalByRefObject, IDisposable
private readonly object _lastTaskGuard = new object();
private Task<EvaluationState> _lastTask;
private struct EvaluationState
private sealed class ServiceState : IDisposable
{
public readonly InteractiveAssemblyLoader AssemblyLoader;
public readonly MetadataShadowCopyProvider MetadataFileProvider;
public readonly ReplServiceProvider ReplServiceProvider;
public readonly InteractiveScriptGlobals Globals;
public ServiceState(InteractiveAssemblyLoader assemblyLoader, MetadataShadowCopyProvider metadataFileProvider, ReplServiceProvider replServiceProvider, InteractiveScriptGlobals globals)
{
internal ImmutableArray<string> SourceSearchPaths;
internal ImmutableArray<string> ReferenceSearchPaths;
internal string WorkingDirectory;
internal readonly ScriptState<object> ScriptStateOpt;
AssemblyLoader = assemblyLoader;
MetadataFileProvider = metadataFileProvider;
ReplServiceProvider = replServiceProvider;
Globals = globals;
}
public void Dispose()
=> MetadataFileProvider.Dispose();
}
private readonly struct EvaluationState
{
internal readonly ImmutableArray<string> SourceSearchPaths;
internal readonly ImmutableArray<string> ReferenceSearchPaths;
internal readonly string WorkingDirectory;
internal readonly ScriptState<object>? ScriptState;
internal readonly ScriptOptions ScriptOptions;
internal EvaluationState(
ScriptState<object> scriptStateOpt,
ScriptState<object>? scriptState,
ScriptOptions scriptOptions,
ImmutableArray<string> sourceSearchPaths,
ImmutableArray<string> referenceSearchPaths,
string workingDirectory)
{
Debug.Assert(scriptOptions != null);
Debug.Assert(!sourceSearchPaths.IsDefault);
Debug.Assert(!referenceSearchPaths.IsDefault);
Debug.Assert(workingDirectory != null);
ScriptStateOpt = scriptStateOpt;
ScriptState = scriptState;
ScriptOptions = scriptOptions;
SourceSearchPaths = sourceSearchPaths;
ReferenceSearchPaths = referenceSearchPaths;
......@@ -81,8 +95,6 @@ private struct EvaluationState
internal EvaluationState WithScriptState(ScriptState<object> state)
{
Debug.Assert(state != null);
return new EvaluationState(
state,
ScriptOptions,
......@@ -93,10 +105,8 @@ internal EvaluationState WithScriptState(ScriptState<object> state)
internal EvaluationState WithOptions(ScriptOptions options)
{
Debug.Assert(options != null);
return new EvaluationState(
ScriptStateOpt,
ScriptState,
options,
SourceSearchPaths,
ReferenceSearchPaths,
......@@ -115,7 +125,7 @@ internal EvaluationState WithOptions(ScriptOptions options)
public Service()
{
var initialState = new EvaluationState(
scriptStateOpt: null,
scriptState: null,
scriptOptions: ScriptOptions.Default,
sourceSearchPaths: ImmutableArray<string>.Empty,
referenceSearchPaths: ImmutableArray<string>.Empty,
......@@ -140,34 +150,36 @@ private void HandleProcessExit(object sender, EventArgs e)
public void Dispose()
{
_metadataFileProvider.Dispose();
_serviceState?.Dispose();
_serviceState = null;
}
public override object InitializeLifetimeService()
public override object? InitializeLifetimeService()
{
return null;
}
public void Initialize(Type replServiceProviderType, string cultureName)
{
Debug.Assert(replServiceProviderType != null);
Debug.Assert(cultureName != null);
Debug.Assert(_metadataFileProvider == null);
Debug.Assert(_assemblyLoader == null);
Debug.Assert(_replServiceProvider == null);
Contract.ThrowIfFalse(_serviceState == null, "Service already initialized");
// TODO (tomat): we should share the copied files with the host
_metadataFileProvider = new MetadataShadowCopyProvider(
var metadataFileProvider = new MetadataShadowCopyProvider(
Path.Combine(Path.GetTempPath(), "InteractiveHostShadow"),
noShadowCopyDirectories: s_systemNoShadowCopyDirectories,
documentationCommentsCulture: new CultureInfo(cultureName));
_assemblyLoader = new InteractiveAssemblyLoader(_metadataFileProvider);
var assemblyLoader = new InteractiveAssemblyLoader(metadataFileProvider);
var replServiceProvider = (ReplServiceProvider)Activator.CreateInstance(replServiceProviderType);
var globals = new InteractiveScriptGlobals(Console.Out, replServiceProvider.ObjectFormatter);
_replServiceProvider = (ReplServiceProvider)Activator.CreateInstance(replServiceProviderType);
_serviceState = new ServiceState(assemblyLoader, metadataFileProvider, replServiceProvider, globals);
}
_globals = new InteractiveScriptGlobals(Console.Out, _replServiceProvider.ObjectFormatter);
private ServiceState GetServiceState()
{
Contract.ThrowIfNull(_serviceState, "Service not initialized");
return _serviceState;
}
private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory)
......@@ -177,7 +189,7 @@ private MetadataReferenceResolver CreateMetadataReferenceResolver(ImmutableArray
packageResolver: null,
gacFileResolver: GacFileResolver.IsAvailable ? new GacFileResolver(preferredCulture: CultureInfo.CurrentCulture) : null,
useCoreResolver: !GacFileResolver.IsAvailable,
fileReferenceProvider: (path, properties) => new ShadowCopyReference(_metadataFileProvider, path, properties));
fileReferenceProvider: (path, properties) => new ShadowCopyReference(GetServiceState().MetadataFileProvider, path, properties));
}
private SourceReferenceResolver CreateSourceReferenceResolver(ImmutableArray<string> searchPaths, string baseDirectory)
......@@ -198,10 +210,7 @@ private static bool AttachToClientProcess(int clientProcessId)
}
clientProcess.EnableRaisingEvents = true;
clientProcess.Exited += new EventHandler((_, __) =>
{
s_clientExited.Set();
});
clientProcess.Exited += new EventHandler((_, __) => s_clientExited.Set());
return clientProcess.IsAlive();
}
......@@ -240,8 +249,8 @@ private static void RunServer(string serverPort, string semaphoreName, int clien
SetErrorMode(GetErrorMode() | ErrorMode.SEM_FAILCRITICALERRORS | ErrorMode.SEM_NOOPENFILEERRORBOX | ErrorMode.SEM_NOGPFAULTERRORBOX);
}
IpcServerChannel serverChannel = null;
IpcClientChannel clientChannel = null;
IpcServerChannel? serverChannel = null;
IpcClientChannel? clientChannel = null;
try
{
using (var semaphore = Semaphore.OpenExisting(semaphoreName))
......@@ -325,11 +334,6 @@ private static string GenerateUniqueChannelLocalName()
string[] sourceSearchPaths,
string baseDirectory)
{
Debug.Assert(operation != null);
Debug.Assert(referenceSearchPaths != null);
Debug.Assert(sourceSearchPaths != null);
Debug.Assert(baseDirectory != null);
lock (_lastTaskGuard)
{
_lastTask = SetPathsAsync(_lastTask, operation, referenceSearchPaths, sourceSearchPaths, baseDirectory);
......@@ -343,17 +347,20 @@ private static string GenerateUniqueChannelLocalName()
string[] sourceSearchPaths,
string baseDirectory)
{
var serviceState = GetServiceState();
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
try
{
Directory.SetCurrentDirectory(baseDirectory);
_globals.ReferencePaths.Clear();
_globals.ReferencePaths.AddRange(referenceSearchPaths);
var referencePaths = serviceState.Globals.ReferencePaths;
referencePaths.Clear();
referencePaths.AddRange(referenceSearchPaths);
_globals.SourcePaths.Clear();
_globals.SourcePaths.AddRange(sourceSearchPaths);
var sourcePaths = serviceState.Globals.SourcePaths;
sourcePaths.Clear();
sourcePaths.AddRange(sourceSearchPaths);
}
finally
{
......@@ -368,10 +375,8 @@ private static string GenerateUniqueChannelLocalName()
/// Execution is performed on the UI thread.
/// </summary>
[OneWay]
public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operation, string initializationFile, bool isRestarting)
public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operation, string? initializationFile, bool isRestarting)
{
Debug.Assert(operation != null);
lock (_lastTaskGuard)
{
_lastTask = InitializeContextAsync(_lastTask, operation, initializationFile, isRestarting);
......@@ -384,9 +389,6 @@ public void InitializeContext(RemoteAsyncOperation<RemoteExecutionResult> operat
[OneWay]
public void AddReference(RemoteAsyncOperation<bool> operation, string reference)
{
Debug.Assert(operation != null);
Debug.Assert(reference != null);
lock (_lastTaskGuard)
{
_lastTask = AddReferenceAsync(_lastTask, operation, reference);
......@@ -429,9 +431,6 @@ private async Task<EvaluationState> AddReferenceAsync(Task<EvaluationState> last
[OneWay]
public void Execute(RemoteAsyncOperation<RemoteExecutionResult> operation, string text)
{
Debug.Assert(operation != null);
Debug.Assert(text != null);
lock (_lastTaskGuard)
{
_lastTask = ExecuteAsync(_lastTask, operation, text);
......@@ -445,7 +444,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask,
bool success = false;
try
{
Script<object> script = TryCompile(state.ScriptStateOpt?.Script, text, null, state.ScriptOptions);
Script<object>? script = TryCompile(state.ScriptState?.Script, text, null, state.ScriptOptions);
if (script != null)
{
// successful if compiled
......@@ -454,7 +453,7 @@ private async Task<EvaluationState> ExecuteAsync(Task<EvaluationState> lastTask,
// remove references and imports from the options, they have been applied and will be inherited from now on:
state = state.WithOptions(state.ScriptOptions.RemoveImportsAndReferences());
var newScriptState = await ExecuteOnUIThreadAsync(script, state.ScriptStateOpt, displayResult: true).ConfigureAwait(false);
var newScriptState = await ExecuteOnUIThreadAsync(script, state.ScriptState, displayResult: true).ConfigureAwait(false);
state = state.WithScriptState(newScriptState);
}
}
......@@ -478,7 +477,7 @@ private void DisplayException(Exception e)
}
else
{
Console.Error.Write(_replServiceProvider.ObjectFormatter.FormatException(e));
Console.Error.Write(GetServiceState().ReplServiceProvider.ObjectFormatter.FormatException(e));
}
}
......@@ -488,9 +487,6 @@ private void DisplayException(Exception e)
[OneWay]
public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, string path)
{
Debug.Assert(operation != null);
Debug.Assert(path != null);
lock (_lastTaskGuard)
{
_lastTask = ExecuteFileAsync(operation, _lastTask, path);
......@@ -500,8 +496,9 @@ public void ExecuteFile(RemoteAsyncOperation<RemoteExecutionResult> operation, s
private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOperation<RemoteExecutionResult> operation, bool success)
{
// send any updates to the host object and current directory back to the client:
var currentSourcePaths = _globals.SourcePaths.ToArray();
var currentReferencePaths = _globals.ReferencePaths.ToArray();
var globals = GetServiceState().Globals;
var currentSourcePaths = globals.SourcePaths.ToArray();
var currentReferencePaths = globals.ReferencePaths.ToArray();
var currentWorkingDirectory = Directory.GetCurrentDirectory();
var changedSourcePaths = currentSourcePaths.SequenceEqual(state.SourceSearchPaths) ? null : currentSourcePaths;
......@@ -532,7 +529,7 @@ private EvaluationState CompleteExecution(EvaluationState state, RemoteAsyncOper
}
return new EvaluationState(
state.ScriptStateOpt,
state.ScriptState,
newOptions,
newSourcePaths,
newReferencePaths,
......@@ -571,11 +568,11 @@ private static void ReportUnhandledException(Exception e)
private async Task<EvaluationState> InitializeContextAsync(
Task<EvaluationState> lastTask,
RemoteAsyncOperation<RemoteExecutionResult> operation,
string initializationFileOpt,
string? initializationFile,
bool isRestarting)
{
Debug.Assert(initializationFileOpt == null || PathUtilities.IsAbsolute(initializationFileOpt));
Contract.ThrowIfFalse(initializationFile == null || PathUtilities.IsAbsolute(initializationFile));
var serviceState = GetServiceState();
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
try
......@@ -584,18 +581,18 @@ private static void ReportUnhandledException(Exception e)
if (!isRestarting)
{
Console.Out.WriteLine(_replServiceProvider.Logo);
Console.Out.WriteLine(serviceState.ReplServiceProvider.Logo);
}
if (File.Exists(initializationFileOpt))
if (File.Exists(initializationFile))
{
Console.Out.WriteLine(string.Format(InteractiveHostResources.Loading_context_from_0, Path.GetFileName(initializationFileOpt)));
var parser = _replServiceProvider.CommandLineParser;
Console.Out.WriteLine(string.Format(InteractiveHostResources.Loading_context_from_0, Path.GetFileName(initializationFile)));
var parser = serviceState.ReplServiceProvider.CommandLineParser;
// The base directory for relative paths is the directory that contains the .rsp file.
// Note that .rsp files included by this .rsp file will share the base directory (Dev10 behavior of csc/vbc).
var rspDirectory = Path.GetDirectoryName(initializationFileOpt);
var args = parser.Parse(new[] { "@" + initializationFileOpt }, rspDirectory, RuntimeEnvironment.GetRuntimeDirectory(), null);
var rspDirectory = Path.GetDirectoryName(initializationFile);
var args = parser.Parse(new[] { "@" + initializationFile }, rspDirectory, RuntimeEnvironment.GetRuntimeDirectory(), null);
foreach (var error in args.Errors)
{
......@@ -624,7 +621,7 @@ private static void ReportUnhandledException(Exception e)
var scriptPathOpt = args.SourceFiles.IsEmpty ? null : args.SourceFiles[0].Path;
var rspState = new EvaluationState(
state.ScriptStateOpt,
state.ScriptState,
state.ScriptOptions.
WithFilePath(scriptPathOpt).
WithReferences(metadataReferences).
......@@ -635,13 +632,14 @@ private static void ReportUnhandledException(Exception e)
args.ReferencePaths,
rspDirectory);
_globals.ReferencePaths.Clear();
_globals.ReferencePaths.AddRange(args.ReferencePaths);
var globals = serviceState.Globals;
globals.ReferencePaths.Clear();
globals.ReferencePaths.AddRange(args.ReferencePaths);
_globals.SourcePaths.Clear();
_globals.SourcePaths.AddRange(args.SourcePaths);
globals.SourcePaths.Clear();
globals.SourcePaths.AddRange(args.SourcePaths);
_globals.Args.AddRange(args.ScriptArguments);
globals.Args.AddRange(args.ScriptArguments);
if (scriptPathOpt != null)
{
......@@ -676,16 +674,16 @@ private static void ReportUnhandledException(Exception e)
return state;
}
private string ResolveRelativePath(string path, string baseDirectory, ImmutableArray<string> searchPaths, bool displayPath)
private string? ResolveRelativePath(string path, string baseDirectory, ImmutableArray<string> searchPaths, bool displayPath)
{
List<string> attempts = new List<string>();
var attempts = new List<string>();
bool fileExists(string file)
{
attempts.Add(file);
return File.Exists(file);
}
string fullPath = FileUtilities.ResolveRelativePath(path, null, baseDirectory, searchPaths, fileExists);
var fullPath = FileUtilities.ResolveRelativePath(path, null, baseDirectory, searchPaths, fileExists);
if (fullPath == null)
{
if (displayPath)
......@@ -706,8 +704,10 @@ bool fileExists(string file)
return fullPath;
}
private Script<object> TryCompile(Script previousScript, string code, string path, ScriptOptions options)
private Script<object>? TryCompile(Script? previousScript, string code, string? path, ScriptOptions options)
{
var serviceState = GetServiceState();
Script script;
var scriptOptions = options.WithFilePath(path);
......@@ -718,7 +718,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
}
else
{
script = _replServiceProvider.CreateScript<object>(code, scriptOptions, _globals.GetType(), _assemblyLoader);
script = serviceState.ReplServiceProvider.CreateScript<object>(code, scriptOptions, serviceState.Globals.GetType(), serviceState.AssemblyLoader);
}
var diagnostics = script.Compile();
......@@ -737,7 +737,7 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
string path)
{
var state = await ReportUnhandledExceptionIfAnyAsync(lastTask).ConfigureAwait(false);
string fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false);
var fullPath = ResolveRelativePath(path, state.WorkingDirectory, state.SourceSearchPaths, displayPath: false);
if (fullPath != null)
{
var newScriptState = await TryExecuteFileAsync(state, fullPath).ConfigureAwait(false);
......@@ -757,18 +757,16 @@ private Script<object> TryCompile(Script previousScript, string code, string pat
/// All errors are written to the error output stream.
/// Uses source search paths to resolve unrooted paths.
/// </remarks>
private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState state, string fullPath)
private async Task<ScriptState<object>?> TryExecuteFileAsync(EvaluationState state, string fullPath)
{
Debug.Assert(PathUtilities.IsAbsolute(fullPath));
string content = null;
string? content = null;
try
{
using (var reader = File.OpenText(fullPath))
{
using var reader = File.OpenText(fullPath);
content = await reader.ReadToEndAsync().ConfigureAwait(false);
}
}
catch (Exception e)
{
// file read errors:
......@@ -776,14 +774,14 @@ private async Task<ScriptState<object>> TryExecuteFileAsync(EvaluationState stat
return null;
}
Script<object> script = TryCompile(state.ScriptStateOpt?.Script, content, fullPath, state.ScriptOptions);
Script<object>? script = TryCompile(state.ScriptState?.Script, content, fullPath, state.ScriptOptions);
if (script == null)
{
// compilation errors:
return null;
}
return await ExecuteOnUIThreadAsync(script, state.ScriptStateOpt, displayResult: false).ConfigureAwait(false);
return await ExecuteOnUIThreadAsync(script, state.ScriptState, displayResult: false).ConfigureAwait(false);
}
private static void DisplaySearchPaths(TextWriter writer, List<string> attemptedFilePaths)
......@@ -805,14 +803,18 @@ private static void DisplaySearchPaths(TextWriter writer, List<string> attempted
}
}
private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> script, ScriptState<object> stateOpt, bool displayResult)
private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> script, ScriptState<object>? state, bool displayResult)
{
Contract.ThrowIfNull(s_control, "UI thread not initialized");
return await ((Task<ScriptState<object>>)s_control.Invoke(
(Func<Task<ScriptState<object>>>)(async () =>
{
var task = (stateOpt == null) ?
script.RunAsync(_globals, catchException: e => true, cancellationToken: CancellationToken.None) :
script.RunFromAsync(stateOpt, catchException: e => true, cancellationToken: CancellationToken.None);
var serviceState = GetServiceState();
var task = (state == null) ?
script.RunAsync(serviceState.Globals, catchException: e => true, cancellationToken: CancellationToken.None) :
script.RunFromAsync(state, catchException: e => true, cancellationToken: CancellationToken.None);
var newState = await task.ConfigureAwait(false);
......@@ -822,7 +824,7 @@ private async Task<ScriptState<object>> ExecuteOnUIThreadAsync(Script<object> sc
}
else if (displayResult && newState.Script.HasReturnValue())
{
_globals.Print(newState.ReturnValue);
serviceState.Globals.Print(newState.ReturnValue);
}
return newState;
......@@ -841,7 +843,7 @@ private void DisplayInteractiveErrors(ImmutableArray<Diagnostic> diagnostics, Te
displayedDiagnostics.Sort((d1, d2) => d1.Location.SourceSpan.Start - d2.Location.SourceSpan.Start);
var formatter = _replServiceProvider.DiagnosticFormatter;
var formatter = GetServiceState().ReplServiceProvider.DiagnosticFormatter;
foreach (var diagnostic in displayedDiagnostics)
{
......@@ -929,11 +931,6 @@ public void RemoteConsoleWrite(byte[] data, bool isError)
}
}
public bool IsShadowCopy(string path)
{
return _metadataFileProvider.IsShadowCopy(path);
}
#endregion
}
}
......
......@@ -2,7 +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.
using System.Diagnostics;
#nullable enable
using Microsoft.CodeAnalysis.Scripting.Hosting;
namespace Microsoft.CodeAnalysis.Interactive
......@@ -20,9 +21,6 @@ private sealed class ShadowCopyReference : PortableExecutableReference
public ShadowCopyReference(MetadataShadowCopyProvider provider, string originalPath, MetadataReferenceProperties properties)
: base(properties, originalPath)
{
Debug.Assert(originalPath != null);
Debug.Assert(provider != null);
_provider = provider;
}
......@@ -39,7 +37,7 @@ protected override Metadata GetMetadataImpl()
protected override PortableExecutableReference WithPropertiesImpl(MetadataReferenceProperties properties)
{
return new ShadowCopyReference(_provider, this.FilePath, properties);
return new ShadowCopyReference(_provider, FilePath!, properties);
}
}
}
......
......@@ -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 System;
using System.Diagnostics;
using System.Globalization;
......@@ -24,7 +26,7 @@ namespace Microsoft.CodeAnalysis.Interactive
/// <remarks>
/// Handles spawning of the host process and communication between the local callers and the remote session.
/// </remarks>
internal sealed partial class InteractiveHost : MarshalByRefObject
internal sealed partial class InteractiveHost
{
internal const bool DefaultIs64Bit = true;
......@@ -35,11 +37,11 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
private readonly int _millisecondsTimeout;
private const int MaxAttemptsToCreateProcess = 2;
private LazyRemoteService _lazyRemoteService;
private LazyRemoteService? _lazyRemoteService;
private int _remoteServiceInstanceId;
// Remoting channel to communicate with the remote service.
private IpcServerChannel _serverChannel;
private IpcServerChannel? _serverChannel;
private TextWriter _output;
private TextWriter _errorOutput;
......@@ -55,7 +57,7 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
/// </remarks>
private readonly bool _joinOutputWritingThreadsOnDisposal;
internal event Action<bool> ProcessStarting;
internal event Action<bool>? ProcessStarting;
public InteractiveHost(
Type replServiceProviderType,
......@@ -79,49 +81,39 @@ internal sealed partial class InteractiveHost : MarshalByRefObject
#region Test hooks
internal event Action<char[], int> OutputReceived;
internal event Action<char[], int> ErrorOutputReceived;
internal event Action<char[], int>? OutputReceived;
internal event Action<char[], int>? ErrorOutputReceived;
internal Process TryGetProcess()
internal Process? TryGetProcess()
{
InitializedRemoteService initializedService;
var lazyRemoteService = _lazyRemoteService;
return (lazyRemoteService?.InitializedService != null &&
lazyRemoteService.InitializedService.TryGetValue(out initializedService)) ? initializedService.ServiceOpt.Process : null;
lazyRemoteService.InitializedService.TryGetValue(out var initializedService)) ? initializedService.Service.Process : null;
}
internal Service TryGetService()
internal Service? TryGetService()
{
var initializedService = TryGetOrCreateRemoteServiceAsync().Result;
return initializedService.ServiceOpt?.Service;
return initializedService.Service?.Service;
}
// Triggered whenever we create a fresh process.
// The ProcessExited event is not hooked yet.
internal event Action<Process> InteractiveHostProcessCreated;
internal event Action<Process>? InteractiveHostProcessCreated;
internal IpcServerChannel _ServerChannel
{
get { return _serverChannel; }
}
internal IpcServerChannel? Test_ServerChannel
=> _serverChannel;
#endregion
private static string GenerateUniqueChannelLocalName()
{
return typeof(InteractiveHost).FullName + Guid.NewGuid();
}
public override object InitializeLifetimeService()
{
return null;
}
=> typeof(InteractiveHost).FullName + Guid.NewGuid();
private RemoteService TryStartProcess(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
private RemoteService? TryStartProcess(string hostPath, CultureInfo culture, CancellationToken cancellationToken)
{
Process newProcess = null;
Process? newProcess = null;
int newProcessId = -1;
Semaphore semaphore = null;
Semaphore? semaphore = null;
try
{
int currentProcessId = Process.GetCurrentProcess().Id;
......@@ -309,7 +301,7 @@ internal void OnOutputReceived(bool error, char[] buffer, int count)
}
}
private void WriteOutputInBackground(bool isError, string firstLine, string secondLine = null)
private void WriteOutputInBackground(bool isError, string firstLine, string? secondLine = null)
{
var writer = isError ? _errorOutput : _output;
var guard = isError ? _errorOutputGuard : _outputGuard;
......@@ -364,7 +356,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
{
try
{
LazyRemoteService currentRemoteService = _lazyRemoteService;
LazyRemoteService? currentRemoteService = _lazyRemoteService;
for (int attempt = 0; attempt < MaxAttemptsToCreateProcess; attempt++)
{
......@@ -375,7 +367,7 @@ private async Task<InitializedRemoteService> TryGetOrCreateRemoteServiceAsync()
}
var initializedService = await currentRemoteService.InitializedService.GetValueAsync(currentRemoteService.CancellationSource.Token).ConfigureAwait(false);
if (initializedService.ServiceOpt != null && initializedService.ServiceOpt.Process.IsAlive())
if (initializedService.Service != null && initializedService.Service.Process.IsAlive())
{
return initializedService;
}
......@@ -418,12 +410,12 @@ private async Task<TResult> Async<TResult>(Action<Service, RemoteAsyncOperation<
try
{
var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedService.ServiceOpt == null)
if (initializedService.Service == null)
{
return default;
return default!;
}
return await new RemoteAsyncOperation<TResult>(initializedService.ServiceOpt).ExecuteAsync(action).ConfigureAwait(false);
return await new RemoteAsyncOperation<TResult>(initializedService.Service).ExecuteAsync(action).ConfigureAwait(false);
}
catch (Exception e) when (FatalError.Report(e))
{
......@@ -445,7 +437,7 @@ private static async Task<TResult> Async<TResult>(RemoteService remoteService, A
#region Operations
public InteractiveHostOptions OptionsOpt
public InteractiveHostOptions? OptionsOpt
=> _lazyRemoteService?.Options;
/// <summary>
......@@ -454,8 +446,6 @@ public InteractiveHostOptions OptionsOpt
/// <param name="options">The options to initialize the new process with.</param>
public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions options)
{
Debug.Assert(options != null);
try
{
// replace the existing service with a new one:
......@@ -468,7 +458,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio
}
var initializedService = await TryGetOrCreateRemoteServiceAsync().ConfigureAwait(false);
if (initializedService.ServiceOpt == null)
if (initializedService.Service == null)
{
return default;
}
......@@ -491,7 +481,7 @@ public async Task<RemoteExecutionResult> ResetAsync(InteractiveHostOptions optio
/// </remarks>
public Task<RemoteExecutionResult> ExecuteAsync(string code)
{
Debug.Assert(code != null);
Contract.ThrowIfNull(code);
return Async<RemoteExecutionResult>((service, operation) => service.Execute(operation, code));
}
......@@ -506,11 +496,7 @@ public Task<RemoteExecutionResult> ExecuteAsync(string code)
/// </remarks>
public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
Contract.ThrowIfNull(path);
return Async<RemoteExecutionResult>((service, operation) => service.ExecuteFile(operation, path));
}
......@@ -524,7 +510,7 @@ public Task<RemoteExecutionResult> ExecuteFileAsync(string path)
/// </remarks>
public Task<bool> AddReferenceAsync(string reference)
{
Debug.Assert(reference != null);
Contract.ThrowIfNull(reference);
return Async<bool>((service, operation) => service.AddReference(operation, reference));
}
......@@ -533,9 +519,9 @@ public Task<bool> AddReferenceAsync(string reference)
/// </summary>
public Task<RemoteExecutionResult> SetPathsAsync(string[] referenceSearchPaths, string[] sourceSearchPaths, string baseDirectory)
{
Debug.Assert(referenceSearchPaths != null);
Debug.Assert(sourceSearchPaths != null);
Debug.Assert(baseDirectory != null);
Contract.ThrowIfNull(referenceSearchPaths);
Contract.ThrowIfNull(sourceSearchPaths);
Contract.ThrowIfNull(baseDirectory);
return Async<RemoteExecutionResult>((service, operation) => service.SetPaths(operation, referenceSearchPaths, sourceSearchPaths, baseDirectory));
}
......
......@@ -2,10 +2,13 @@
// 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.Diagnostics;
using System.Globalization;
using System.IO;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Interactive
{
......@@ -17,7 +20,7 @@ internal sealed class InteractiveHostOptions
/// <summary>
/// Optional path to the .rsp file to process when initializing context of the process.
/// </summary>
public string InitializationFile { get; }
public string? InitializationFile { get; }
/// <summary>
/// Host culture used for localization of doc comments, errors.
......@@ -36,11 +39,11 @@ internal sealed class InteractiveHostOptions
public InteractiveHostOptions(
string hostDirectory,
string initializationFile = null,
CultureInfo culture = null,
string? initializationFile = null,
CultureInfo? culture = null,
bool is64Bit = false)
{
Debug.Assert(hostDirectory != null);
Contract.ThrowIfNull(hostDirectory);
HostDirectory = hostDirectory;
InitializationFile = initializationFile;
Culture = culture ?? CultureInfo.CurrentUICulture;
......
......@@ -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 System.Diagnostics;
namespace Microsoft.CodeAnalysis.Interactive
......
......@@ -2,40 +2,42 @@
// 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;
namespace Microsoft.CodeAnalysis.Interactive
{
[Serializable]
internal struct RemoteExecutionResult
internal readonly struct RemoteExecutionResult
{
public readonly bool Success;
/// <summary>
/// New value of source search paths after execution, or null if not changed since the last execution.
/// </summary>
public readonly string[] ChangedSourcePaths;
public readonly string[]? ChangedSourcePaths;
/// <summary>
/// New value of reference search paths after execution, or null if not changed since the last execution.
/// </summary>
public readonly string[] ChangedReferencePaths;
public readonly string[]? ChangedReferencePaths;
/// <summary>
/// New value of working directory in the remote process after execution, or null if not changed since the last execution.
/// </summary>
public readonly string ChangedWorkingDirectory;
public readonly string? ChangedWorkingDirectory;
public RemoteExecutionResult(
bool success,
string[] changedSourcePaths = null,
string[] changedReferencePaths = null,
string changedWorkingDirectory = null)
string[]? changedSourcePaths = null,
string[]? changedReferencePaths = null,
string? changedWorkingDirectory = null)
{
this.Success = success;
this.ChangedSourcePaths = changedSourcePaths;
this.ChangedReferencePaths = changedReferencePaths;
this.ChangedWorkingDirectory = changedWorkingDirectory;
Success = success;
ChangedSourcePaths = changedSourcePaths;
ChangedReferencePaths = changedReferencePaths;
ChangedWorkingDirectory = changedWorkingDirectory;
}
}
}
// 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
extern alias InteractiveHost;
using System;
......@@ -32,7 +35,7 @@ internal static string GetInteractiveHostDirectory()
internal static void DisposeInteractiveHostProcess(InteractiveHost host)
{
var serverChannel = host._ServerChannel;
var serverChannel = host.Test_ServerChannel;
host.Dispose();
var listenerThread = (Thread)s_ipcServerChannelListenerThread.GetValue(serverChannel);
......
......@@ -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
extern alias InteractiveHost;
using System;
......@@ -34,8 +36,8 @@ public sealed class InteractiveHostTests : AbstractInteractiveHostTests
{
#region Utils
private SynchronizedStringWriter _synchronizedOutput;
private SynchronizedStringWriter _synchronizedErrorOutput;
private SynchronizedStringWriter _synchronizedOutput = null!;
private SynchronizedStringWriter _synchronizedErrorOutput = null!;
private int[] _outputReadPosition = new int[] { 0, 0 };
private readonly InteractiveHost _host;
......@@ -75,7 +77,7 @@ public override void Dispose()
{
try
{
Process process = _host.TryGetProcess();
var process = _host.TryGetProcess();
DisposeInteractiveHostProcess(_host);
......@@ -118,11 +120,6 @@ private bool Execute(string code)
return task.Result.Success;
}
private bool IsShadowCopy(string path)
{
return _host.TryGetService().IsShadowCopy(path);
}
public string ReadErrorOutputToEnd()
{
return ReadOutputToEnd(isError: true);
......@@ -135,7 +132,7 @@ private void ClearOutput()
_synchronizedErrorOutput.Clear();
}
private void RestartHost(string rspFile = null)
private void RestartHost(string? rspFile = null)
{
ClearOutput();
......@@ -150,7 +147,7 @@ public string ReadOutputToEnd(bool isError = false)
var mark = markPrefix + Guid.NewGuid().ToString();
// writes mark to the STDOUT/STDERR pipe in the remote process:
_host.TryGetService().RemoteConsoleWrite(Encoding.UTF8.GetBytes(mark), isError);
_host.TryGetService()!.RemoteConsoleWrite(Encoding.UTF8.GetBytes(mark), isError);
while (true)
{
......@@ -164,13 +161,8 @@ public string ReadOutputToEnd(bool isError = false)
}
}
private class CompiledFile
{
public string Path;
public ImmutableArray<byte> Image;
}
private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references)
private static (string Path, ImmutableArray<byte> Image) CompileLibrary(
TempDirectory dir, string fileName, string assemblyName, string source, params MetadataReference[] references)
{
var file = dir.CreateFile(fileName);
var compilation = CreateEmptyCompilation(
......@@ -182,7 +174,7 @@ private static CompiledFile CompileLibrary(TempDirectory dir, string fileName, s
var image = compilation.EmitToArray();
file.WriteAllBytes(image);
return new CompiledFile { Path = file.Path, Image = image };
return (file.Path, image);
}
#endregion
......@@ -250,7 +242,7 @@ int goo(int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8,
// Hosting process exited with exit code ###.
var errorOutput = ReadErrorOutputToEnd().Trim();
Assert.Equal("Process is terminated due to StackOverflowException.\n" + string.Format(InteractiveHostResources.Hosting_process_exited_with_exit_code_0, process.ExitCode), errorOutput);
Assert.Equal("Process is terminated due to StackOverflowException.\n" + string.Format(InteractiveHostResources.Hosting_process_exited_with_exit_code_0, process!.ExitCode), errorOutput);
Execute(@"1+1");
......@@ -337,10 +329,10 @@ public void AsyncExecute_HangingForegroundThreads()
var process = _host.TryGetProcess();
Assert.NotNull(process);
service.EmulateClientExit();
service!.EmulateClientExit();
// the process should terminate with exit code 0:
process.WaitForExit();
process!.WaitForExit();
Assert.Equal(0, process.ExitCode);
}
......@@ -454,7 +446,7 @@ public void UserDefinedAssemblyResolve_InfiniteLoop()
var mayTerminate = new ManualResetEvent(false);
_host.ErrorOutputReceived += (_, __) => mayTerminate.Set();
_host.TryGetService().HookMaliciousAssemblyResolve();
_host.TryGetService()!.HookMaliciousAssemblyResolve();
var executeTask = _host.AddReferenceAsync("nonexistingassembly" + Guid.NewGuid());
Assert.True(mayTerminate.WaitOne());
......
// 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
extern alias InteractiveHost;
using System;
......
......@@ -2,9 +2,10 @@
// 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.IO;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.UnitTests.Interactive
{
......@@ -44,7 +45,7 @@ public override string ToString()
}
}
public string Prefix(string mark, ref int start)
public string? Prefix(string mark, ref int start)
{
Debug.Assert(!string.IsNullOrEmpty(mark));
......
......@@ -223,8 +223,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
}
/// <summary>
/// If <paramref name="symbol"/> is declared in a linked file, then this function returns all the other symbols
/// that are defined by the same symbol's syntax in the other projects that the linked file is referenced from.
/// If <paramref name="symbol"/> is declared in a linked file, then this function returns all the symbols that
/// are defined by the same symbol's syntax in the all projects that the linked file is referenced from.
/// <para/>
/// In order to be returned the other symbols must have the same <see cref="ISymbol.Name"/> and <see
/// cref="ISymbol.Kind"/> as <paramref name="symbol"/>. This matches general user intuition that these are all
......@@ -234,7 +234,8 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
internal static async Task<ImmutableArray<ISymbol>> FindLinkedSymbolsAsync(
ISymbol symbol, Solution solution, CancellationToken cancellationToken)
{
var linkedSymbols = new HashSet<ISymbol>();
// Add the original symbol to the result set.
var linkedSymbols = new HashSet<ISymbol> { symbol };
foreach (var location in symbol.DeclaringSyntaxReferences)
{
......@@ -256,8 +257,9 @@ public static IEnumerable<TSymbol> FindSimilarSymbols<TSymbol>(TSymbol symbol, C
var semanticModel = await linkedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var linkedSymbol = semanticModel.GetDeclaredSymbol(linkedNode, cancellationToken);
if (linkedSymbol?.Kind == symbol.Kind &&
linkedSymbol?.Name == symbol.Name)
if (linkedSymbol != null &&
linkedSymbol.Kind == symbol.Kind &&
linkedSymbol.Name == symbol.Name)
{
linkedSymbols.Add(linkedSymbol);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册