提交 dcffe42f 编写于 作者: C CyrusNajmabadi

Merge branch 'master' into genMethodInference

......@@ -105,29 +105,9 @@
<RunTestsArgs>$(NuGetPackageRoot)\xunit.runner.console\$(xunitrunnerconsoleVersion)\tools $(RunTestArgs) @(TestAssemblies, ' ')</RunTestsArgs>
</PropertyGroup>
<Exec Condition="'$(RunProcessWatchdog)' != 'true' AND '$(TestVsi)' != 'true'" Command="&quot;$(CoreRunExe)&quot; $(CoreRunArgs)" />
<Exec Condition="'$(RunProcessWatchdog)' != 'true'" Command="&quot;$(RunTestsExe)&quot; $(RunTestsArgs)" />
<PropertyGroup Condition="'$(RunProcessWatchdog)' == 'true'">
<ProcessWatchDogExe>$(OutputDirectory)\ProcessWatchdog\ProcessWatchdog.exe</ProcessWatchDogExe>
<ProcessWatchdogOutputDirectory Condition="'$(ProcessWatchdogOutputDirectory)' == ''">$(OutputDirectory)\ProcessWatchdogOutput</ProcessWatchdogOutputDirectory>
<ProcDumpExe Condition="'$(ProcDumpExe)' == ''">C:\Sysinternals\Procdump.exe</ProcDumpExe>
<!--
The time reserved for the process watchdog to kill all test processes and obtain crash
dumps from them, measure in seconds.
-->
<BufferTime>300</BufferTime>
<!--
The fully constructed command line which runs a PowerShell script that runs both the
Core CLR unit tests and the desktop unit tests under the control of the process watchdog.
-->
<RunProcessWatchdogScriptCommand>PowerShell.exe -ExecutionPolicy RemoteSigned -NoProfile .\build\scripts\Run-TestsWithProcessWatchdog.ps1 -ProcessWatchDogExe $(ProcessWatchDogExe) -ProcessWatchdogOutputDirectory $(ProcessWatchdogOutputDirectory) -ProcDumpExe $(ProcDumpExe) -CoreRunExe '$(CoreRunExe)' -CoreRunArgs '$(CoreRunArgs)' -RunTestsExe '$(RunTestsExe)' -RunTestsArgs '$(RunTestsArgs)' -BuildStartTime $(BuildStartTime) -BuildTimeLimit $(BuildTimeLimit) -BufferTime $(BufferTime)</RunProcessWatchdogScriptCommand>
</PropertyGroup>
<Exec Condition="'$(TestVsi)' != 'true'" Command="&quot;$(CoreRunExe)&quot; $(CoreRunArgs)" />
<Exec Condition="'$(RunProcessWatchdog)' == 'true'" Command="$(RunProcessWatchdogScriptCommand)" />
<Exec Command="&quot;$(RunTestsExe)&quot; $(RunTestsArgs)" />
</Target>
......
......@@ -10,7 +10,7 @@ RESTORE_SEMAPHORE_PATH = $(BINARIES_PATH)/restore.semaphore
BOOTSTRAP_PATH = $(BINARIES_PATH)/Bootstrap
BUILD_LOG_PATH =
HOME_DIR = $(shell cd ~ && pwd)
DOTNET_VERSION = 1.0.0-preview2-002911
DOTNET_VERSION = 1.0.0-preview2-003131
NUGET_VERSION = 3.5.0-beta2
NUGET_EXE = $(shell pwd)/nuget.exe
......
@echo off
@if not defined EchoOn @echo off
@setlocal enabledelayedexpansion
set RoslynRoot=%~dp0
......
@echo off
@if not defined EchoOn @echo off
:: Prefer building with Dev15 and try the simple route first (we may be running from a DevCmdPrompt already)
set CommonToolsDir=%VS150COMNTOOLS%
......
......@@ -20,7 +20,7 @@
"Newtonsoft.Json": "8.0.3"
},
"frameworks": {
"NETCoreApp1.0": {
"NETCoreApp1.1": {
"imports": [
"portable-net452",
"dotnet"
......@@ -29,6 +29,7 @@
},
"runtimes": {
"ubuntu.14.04-x64": {},
"osx.10.11-x64": {}
"osx.10.11-x64": {},
"osx.10.12-x64": {}
}
}
......@@ -18,6 +18,7 @@
<MicrosoftVisualBasicVersion>10.0.1</MicrosoftVisualBasicVersion>
<RoslynToolsMicrosoftLocateVSVersion>0.2.0-beta</RoslynToolsMicrosoftLocateVSVersion>
<RoslynToolsMicrosoftSignToolVersion>0.2.0-beta</RoslynToolsMicrosoftSignToolVersion>
<RoslynToolsMicrosoftVSIXExpInstallerVersion>0.2.1-beta</RoslynToolsMicrosoftVSIXExpInstallerVersion>
<SystemAppContextVersion>4.1.0</SystemAppContextVersion>
<SystemCollectionsVersion>4.0.11</SystemCollectionsVersion>
<SystemCollectionsConcurrentVersion>4.0.12</SystemCollectionsConcurrentVersion>
......
......@@ -13,6 +13,8 @@
<NuGetPackagesDirectory>$(NuGetPackageRoot)</NuGetPackagesDirectory>
<ToolsetCompilerPackageName>Microsoft.Net.Compilers</ToolsetCompilerPackageName>
<ToolsetCompilerPackageVersion>2.0.0-rc2-61102-09</ToolsetCompilerPackageVersion>
<VSIXExpInstallerPackageName>RoslynTools.Microsoft.VSIXExpInstaller</VSIXExpInstallerPackageName>
<VSIXExpInstallerPackageVersion>0.2.1-beta</VSIXExpInstallerPackageVersion>
<RoslynDiagnosticsNugetPackageVersion>1.2.0-beta2</RoslynDiagnosticsNugetPackageVersion>
<RoslynDiagnosticsPropsFilePath>$(NuGetPackageRoot)\Microsoft.Net.RoslynDiagnostics\$(RoslynDiagnosticsNugetPackageVersion)\build\Microsoft.Net.RoslynDiagnostics.props</RoslynDiagnosticsPropsFilePath>
<RoslynBuildUtilFilePath>$(NuGetPackageRoot)\Roslyn.Build.Util\0.9.4-portable\lib\dotnet\Roslyn.MSBuild.Util.dll</RoslynBuildUtilFilePath>
......
......@@ -14,7 +14,8 @@
"Roslyn.Build.Util": "0.9.4-portable",
"RoslynDependencies.OptimizationData": "2.0.0-rc-61101-16",
"RoslynTools.Microsoft.LocateVS": "0.2.0-beta",
"RoslynTools.Microsoft.SignTool": "0.2.0-beta"
"RoslynTools.Microsoft.SignTool": "0.2.0-beta",
"RoslynTools.Microsoft.VSIXExpInstaller": "0.2.1-beta"
},
"frameworks": {
"net461": {}
......
......@@ -75,11 +75,11 @@ copy "build\bootstrap\*" "%bindir%\Bootstrap" || goto :BuildFailed
REM Clean the previous build
msbuild %MSBuildAdditionalCommandLineArgs% /t:Clean build/Toolset/Toolset.csproj /p:Configuration=%BuildConfiguration% /fileloggerparameters:LogFile="%bindir%\BootstrapClean.log" || goto :BuildFailed
call :TerminateBuildProcesses
call :TerminateBuildProcesses || goto :BuildFailed
if defined TestDeterminism (
powershell -noprofile -executionPolicy RemoteSigned -file "%RoslynRoot%\build\scripts\test-determinism.ps1" "%bindir%\Bootstrap" || goto :BuildFailed
call :TerminateBuildProcesses
call :TerminateBuildProcesses || goto :BuildFailed
exit /b 0
)
......@@ -110,6 +110,8 @@ if defined TestPerfRun (
)
)
call :TerminateBuildProcesses || goto :BuildFailed
.\Binaries\%BuildConfiguration%\Exes\Perf.Runner\Roslyn.Test.Performance.Runner.exe --no-trace-upload !EXTRA_PERF_RUNNER_ARGS! || goto :BuildFailed
exit /b 0
)
......@@ -117,7 +119,7 @@ if defined TestPerfRun (
msbuild %MSBuildAdditionalCommandLineArgs% /p:BootstrapBuildPath="%bindir%\Bootstrap" BuildAndTest.proj /p:Configuration=%BuildConfiguration% /p:Test64=%Test64% /p:TestVsi=%TestVsi% /p:RunProcessWatchdog=%RunProcessWatchdog% /p:BuildStartTime=%BuildStartTime% /p:"ProcDumpExe=%ProcDumpExe%" /p:BuildTimeLimit=%BuildTimeLimit% /p:PathMap="%RoslynRoot%=q:\roslyn" /p:Feature=pdb-path-determinism /fileloggerparameters:LogFile="%bindir%\Build.log";verbosity=diagnostic /p:DeployExtension=false || goto :BuildFailed
powershell -noprofile -executionPolicy RemoteSigned -file "%RoslynRoot%\build\scripts\check-msbuild.ps1" "%bindir%\Build.log" || goto :BuildFailed
call :TerminateBuildProcesses
call :TerminateBuildProcesses || goto :BuildFailed
REM Ensure caller sees successful exit.
exit /b 0
......@@ -142,5 +144,12 @@ exit /b 1
@REM Kill any instances of msbuild.exe to ensure that we never reuse nodes (e.g. if a non-roslyn CI run
@REM left some floating around).
taskkill /F /IM vbcscompiler.exe 2> nul
taskkill /F /IM msbuild.exe 2> nul
@REM An error-level of 1 means that the process was found, but could not be killed.
echo Killing all build-related processes
taskkill /F /IM msbuild.exe > nul
if %ERRORLEVEL% == 1 exit /b 1
taskkill /F /IM vbcscompiler.exe > nul
if %ERRORLEVEL% == 1 exit /b 1
exit /b 0
......@@ -4411,7 +4411,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
generation but may be used by analyzers for producing
errors or warnings.
/embed Embed all source files in the PDB.
/embed:&lt;file list&gt; Embed specfic files the PDB
/embed:&lt;file list&gt; Embed specific files in the PDB
- RESOURCES -
/win32res:&lt;file&gt; Specify a Win32 resource file (.res)
......@@ -4975,4 +4975,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ThrowMisplaced" xml:space="preserve">
<value>A throw expression is not allowed in this context.</value>
</data>
</root>
\ No newline at end of file
</root>
......@@ -111,6 +111,7 @@
<Compile Include="ExtractInterface\ExtractInterfaceCommandHandler.cs" />
<Compile Include="ExtractMethod\ExtractMethodCommandHandler.cs" />
<Compile Include="FindReferences\CSharpFindReferencesService.cs" />
<Compile Include="FindUsages\CSharpFindUsagesService.cs" />
<Compile Include="Formatting\CSharpEditorFormattingService.cs" />
<Compile Include="Formatting\Indentation\CSharpIndentationService.cs" />
<Compile Include="Formatting\Indentation\CSharpIndentationService.Indenter.cs" />
......
......@@ -2,8 +2,8 @@
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.FindReferences;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.FindReferences
......@@ -14,22 +14,8 @@ internal class CSharpFindReferencesService : AbstractFindReferencesService
[ImportingConstructor]
public CSharpFindReferencesService(
[ImportMany] IEnumerable<IDefinitionsAndReferencesPresenter> referencedSymbolsPresenters,
[ImportMany] IEnumerable<INavigableItemsPresenter> navigableItemsPresenters,
[ImportMany] IEnumerable<IFindReferencesResultProvider> externalReferencesProviders)
: base(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
{
}
}
[ExportLanguageService(typeof(IStreamingFindReferencesService), LanguageNames.CSharp), Shared]
internal class CSharpStreamingFindReferencesService : AbstractFindReferencesService
{
[ImportingConstructor]
public CSharpStreamingFindReferencesService(
[ImportMany] IEnumerable<IDefinitionsAndReferencesPresenter> referencedSymbolsPresenters,
[ImportMany] IEnumerable<INavigableItemsPresenter> navigableItemsPresenters,
[ImportMany] IEnumerable<IFindReferencesResultProvider> externalReferencesProviders)
: base(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
[ImportMany] IEnumerable<INavigableItemsPresenter> navigableItemsPresenters)
: base(referencedSymbolsPresenters, navigableItemsPresenters)
{
}
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.FindSymbols
namespace Microsoft.CodeAnalysis.Editor.CSharp.FindUsages
{
/// <summary>
/// Information about all the declarations defined within a document. Each declaration in the
/// document get a single item in <see cref="IDeclarationInfo.DeclaredSymbolInfos"/>.
/// </summary>
internal interface IDeclarationInfo
[ExportLanguageService(typeof(IFindUsagesService), LanguageNames.CSharp), Shared]
internal class CSharpFindUsagesService : AbstractFindUsagesService
{
ImmutableArray<DeclaredSymbolInfo> DeclaredSymbolInfos { get; }
}
}
\ No newline at end of file
......@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
......@@ -15,7 +15,8 @@ internal class CSharpGoToDefinitionService : AbstractGoToDefinitionService
[ImportingConstructor]
public CSharpGoToDefinitionService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
[ImportMany]IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders) : base(presenters, externalDefinitionProviders)
[ImportMany]IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
: base(presenters, streamingPresenters)
{
}
......@@ -24,4 +25,4 @@ protected override ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, C
return symbol;
}
}
}
}
\ No newline at end of file
......@@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Composition;
using Microsoft.CodeAnalysis.Editor.GoToImplementation;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation;
using Microsoft.CodeAnalysis.Host.Mef;
namespace Microsoft.CodeAnalysis.Editor.CSharp.GoToImplementation
......@@ -14,9 +14,9 @@ internal sealed class CSharpGoToImplementationService : AbstractGoToImplementati
{
[ImportingConstructor]
public CSharpGoToImplementationService(
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
[ImportMany]IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders) : base(presenters, externalDefinitionProviders)
[ImportMany]IEnumerable<Lazy<INavigableItemsPresenter>> presenters)
: base(presenters)
{
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.ExtractMethod;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Test.Utilities;
using Xunit;
......@@ -14,9 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.Extrac
public class ExtractMethodTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace)
{
return new ExtractMethodCodeRefactoringProvider();
}
=> new ExtractMethodCodeRefactoringProvider();
[WorkItem(540799, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540799")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
......@@ -1213,5 +1214,101 @@ private static void NewMethod(int v, CancellationToken ct)
}
}");
}
[WorkItem(15219, "https://github.com/dotnet/roslyn/issues/15219")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestUseVar1()
{
await TestAsync(
@"using System;
class C
{
void Foo(int i)
{
[|var v = (string)null;
switch (i)
{
case 0: v = ""0""; break;
case 1: v = ""1""; break;
}|]
Console.WriteLine(v);
}
}",
@"using System;
class C
{
void Foo(int i)
{
var v = {|Rename:NewMethod|}(i);
Console.WriteLine(v);
}
private static string NewMethod(int i)
{
var v = (string)null;
switch (i)
{
case 0: v = ""0""; break;
case 1: v = ""1""; break;
}
return v;
}
}", options: Option(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithSuggestionEnforcement));
}
[WorkItem(15219, "https://github.com/dotnet/roslyn/issues/15219")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestUseVar2()
{
await TestAsync(
@"using System;
class C
{
void Foo(int i)
{
[|var v = (string)null;
switch (i)
{
case 0: v = ""0""; break;
case 1: v = ""1""; break;
}|]
Console.WriteLine(v);
}
}",
@"using System;
class C
{
void Foo(int i)
{
string v = {|Rename:NewMethod|}(i);
Console.WriteLine(v);
}
private static string NewMethod(int i)
{
var v = (string)null;
switch (i)
{
case 0: v = ""0""; break;
case 1: v = ""1""; break;
}
return v;
}
}", options: Option(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithSuggestionEnforcement));
}
}
}
\ No newline at end of file
......@@ -14,15 +14,7 @@ public class CompletionServiceTests
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public void AcquireCompletionService()
{
var hostServices = MefHostServices.Create(
MefHostServices.DefaultAssemblies.Concat(
new[]
{
typeof(CompletionService).Assembly,
typeof(CSharpCompletionService).Assembly
}));
var workspace = new AdhocWorkspace(hostServices);
var workspace = new AdhocWorkspace();
var document = workspace
.AddProject("TestProject", LanguageNames.CSharp)
......
......@@ -6,6 +6,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.ExtractMethod;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
......@@ -82,7 +83,9 @@ protected async Task NotSupported_ExtractMethodAsync(string codeWithMarker)
var testDocument = workspace.Documents.Single();
var subjectBuffer = testDocument.TextBuffer;
var tree = await ExtractMethodAsync(workspace, testDocument, allowMovingDeclaration: allowMovingDeclaration, dontPutOutOrRefOnStruct: dontPutOutOrRefOnStruct);
var tree = await ExtractMethodAsync(
workspace, testDocument, allowMovingDeclaration: allowMovingDeclaration,
dontPutOutOrRefOnStruct: dontPutOutOrRefOnStruct);
using (var edit = subjectBuffer.CreateEdit())
{
......
......@@ -471,7 +471,7 @@ void M()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestFixAllInDocument()
public async Task TestFixAllInDocument1()
{
await TestAsync(
@"using System.Collections.Generic;
......@@ -510,6 +510,78 @@ void M()
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestFixAllInDocument2()
{
await TestAsync(
@"using System.Collections.Generic;
class C
{
void M()
{
var list1 = {|FixAllInDocument:new|} List<int>(() => {
var list2 = new List<int>();
list2.Add(2);
});
list1.Add(1);
}
}",
@"using System.Collections.Generic;
class C
{
void M()
{
var list1 = new List<int>(() => {
var list2 = new List<int>
{
2
};
})
{
1
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestFixAllInDocument3()
{
await TestAsync(
@"using System.Collections.Generic;
class C
{
void M()
{
var list1 = {|FixAllInDocument:new|} List<int>();
list1.Add(() => {
var list2 = new List<int>();
list2.Add(2);
});
}
}",
@"using System.Collections.Generic;
class C
{
void M()
{
var list1 = new List<int>
{
() => {
var list2 = new List<int>
{
2
};
}
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)]
public async Task TestTrivia1()
{
......
......@@ -305,7 +305,83 @@ void M()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
public async Task TestFixAllInDocument()
public async Task TestFixAllInDocument1()
{
await TestAsync(
@"class C
{
int i;
int j;
void M()
{
var v = {|FixAllInDocument:new|} C(() => {
var v2 = new C();
v2.i = 1;
});
v.j = 2;
}
}",
@"class C
{
int i;
int j;
void M()
{
var v = new C(() => {
var v2 = new C()
{
i = 1
};
})
{
j = 2
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
public async Task TestFixAllInDocument2()
{
await TestAsync(
@"class C
{
int i;
int j;
void M()
{
var v = {|FixAllInDocument:new|} C();
v.j = () => {
var v2 = new C();
v2.i = 1;
};
}
}",
@"class C
{
int i;
int j;
void M()
{
var v = new C()
{
j = () => {
var v2 = new C()
{
i = 1
};
}
};
}
}");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)]
public async Task TestFixAllInDocument3()
{
await TestAsync(
@"class C
......
......@@ -128,6 +128,120 @@ public async Task TestNotAfterPartialInClass()
class Foo
{
partial $$
}");
}
[Fact]
[WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
public async Task TestLocalFunction()
{
await VerifyKeywordAsync(@"
class Foo
{
public void M()
{
$$
}
}");
}
[Fact]
[WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction2()
{
await VerifyKeywordAsync(@"
class Foo
{
public void M()
{
unsafe $$
}
}");
}
[Fact]
[WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction3()
{
await VerifyKeywordAsync(@"
class Foo
{
public void M()
{
unsafe $$ void L() { }
}
}");
}
[Fact]
[WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction4()
{
await VerifyKeywordAsync(@"
class Foo
{
public void M()
{
$$ void L() { }
}
}");
}
[Fact]
[WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction5()
{
await VerifyKeywordAsync(@"
class Foo
{
public void M(Action<int> a)
{
M(async () =>
{
$$
});
}
}");
}
[Fact]
[WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction6()
{
await VerifyAbsenceAsync(@"
class Foo
{
public void M()
{
int $$
}
}");
}
[Fact]
[WorkItem(8616, "https://github.com/dotnet/roslyn/issues/8616")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction7()
{
await VerifyAbsenceAsync(@"
class Foo
{
public void M()
{
static $$
}
}");
}
}
......
......@@ -83,13 +83,6 @@ public async Task TestNotInCastType2()
@"var str = (($$)items) as string;"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestNotInEmptyStatement()
{
await VerifyAbsenceAsync(AddInsideMethod(
@"$$"));
}
[Fact, Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestInTypeOf()
{
......@@ -642,5 +635,123 @@ public async Task TestNotAfterAsyncAsType()
{
await VerifyAbsenceAsync(@"class c { async async $$ }");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction()
{
await VerifyKeywordAsync(@"
class C
{
void M()
{
$$
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[WorkItem(14525, "https://github.com/dotnet/roslyn/issues/14525")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction2()
{
await VerifyKeywordAsync(@"
class C
{
void M()
{
async $$
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction3()
{
await VerifyAbsenceAsync(@"
class C
{
void M()
{
async async $$
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction4()
{
await VerifyAbsenceAsync(@"
class C
{
void M()
{
var async $$
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction5()
{
await VerifyAbsenceAsync(@"
using System;
class C
{
void M(Action<int> a)
{
M(async $$ () =>
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction6()
{
await VerifyKeywordAsync(@"
class C
{
void M()
{
unsafe async $$
}
}");
}
[Fact]
[WorkItem(8617, "https://github.com/dotnet/roslyn/issues/8617")]
[Test.Utilities.CompilerTrait(Test.Utilities.CompilerFeature.LocalFunctions)]
[Trait(Traits.Feature, Traits.Features.KeywordRecommending)]
public async Task TestLocalFunction7()
{
await VerifyKeywordAsync(@"
using System;
class C
{
void M(Action<int> a)
{
M(async () =>
{
async $$
})
}
}");
}
}
}
......@@ -108,6 +108,12 @@
<InternalsVisibleToMoq Include="DynamicProxyGenAssembly2" />
</ItemGroup>
<ItemGroup>
<Compile Include="FindUsages\AbstractFindUsagesService.cs" />
<Compile Include="FindUsages\FindUsagesHelpers.cs" />
<Compile Include="FindUsages\FindUsagesContext.cs" />
<Compile Include="FindUsages\IFindUsagesContext.cs" />
<Compile Include="FindUsages\IFindUsagesService.cs" />
<Compile Include="FindUsages\SimpleFindUsagesContext.cs" />
<Compile Include="Implementation\NavigateTo\AbstractNavigateToItemDisplay.cs" />
<Compile Include="CommandArgs.cs" />
<Compile Include="CommandHandlers\AbstractCompletionCommandHandler.cs" />
......@@ -254,12 +260,9 @@
<Compile Include="Implementation\Diagnostics\DiagnosticsSuggestionTaggerProvider.cs" />
<Compile Include="Implementation\Diagnostics\SuggestionAdornmentManagerProvider.cs" />
<Compile Include="Implementation\Diagnostics\SuggestionTag.cs" />
<Compile Include="Implementation\FindReferences\AbstractFindReferencesService.ProgressAdapter.cs" />
<Compile Include="Implementation\FindReferences\FindReferencesContext.cs" />
<Compile Include="Implementation\FindReferences\IFindReferencesNavigableItemResultProvider.cs" />
<Compile Include="Implementation\GoToDefinition\INavigableDefinitionProvider.cs" />
<Compile Include="Implementation\GoToImplementation\AbstractGoToImplementationService.cs" />
<Compile Include="Implementation\GoToImplementation\IGoToImplementationService.cs" />
<Compile Include="FindUsages\AbstractFindUsagesService.ProgressAdapter.cs" />
<Compile Include="GoToImplementation\AbstractGoToImplementationService.cs" />
<Compile Include="GoToImplementation\IGoToImplementationService.cs" />
<Compile Include="Implementation\Intellisense\Completion\BraceCompletionMetadata.cs" />
<Compile Include="Implementation\Intellisense\Completion\OptionSetExtensions.cs" />
<Compile Include="Implementation\Intellisense\Completion\Presentation\ClassificationTags.cs" />
......@@ -280,7 +283,7 @@
<Compile Include="Implementation\Preview\PreviewReferenceHighlightingTaggerProvider.cs" />
<Compile Include="Implementation\Suggestions\FixAllSuggestedAction.FixAllCodeAction.cs" />
<Compile Include="Implementation\Suggestions\FixMultipleOccurrencesService.cs" />
<Compile Include="Implementation\GoToImplementation\GoToImplementationCommandHandler.cs" />
<Compile Include="GoToImplementation\GoToImplementationCommandHandler.cs" />
<Compile Include="Implementation\TodoComment\ITodoListProvider.cs" />
<Compile Include="Implementation\TodoComment\TodoItem.cs" />
<Compile Include="Implementation\TodoComment\TodoItemsUpdatedArgs.cs" />
......@@ -377,9 +380,9 @@
<Compile Include="Implementation\EndConstructGeneration\IEndConstructGenerationService.cs" />
<Compile Include="Implementation\ExtractInterface\AbstractExtractInterfaceCommandHandler.cs" />
<Compile Include="Implementation\ExtractMethod\AbstractExtractMethodCommandHandler.cs" />
<Compile Include="Implementation\FindReferences\AbstractFindReferencesService.cs" />
<Compile Include="Implementation\FindReferences\FindReferencesCommandHandler.cs" />
<Compile Include="Implementation\FindReferences\IFindReferencesService.cs" />
<Compile Include="FindReferences\AbstractFindReferencesService.cs" />
<Compile Include="FindReferences\FindReferencesCommandHandler.cs" />
<Compile Include="FindReferences\IFindReferencesService.cs" />
<Compile Include="Implementation\ForegroundNotification\ForegroundNotificationService.cs" />
<Compile Include="Implementation\Formatting\FormatCommandHandler.cs" />
<Compile Include="Implementation\Formatting\FormatCommandHandler.ReturnKey.cs" />
......@@ -390,10 +393,10 @@
<Compile Include="Implementation\Formatting\Indentation\AbstractSmartTokenFormatterCommandHandler.cs" />
<Compile Include="Implementation\Formatting\Indentation\ISmartTokenFormatter.cs" />
<Compile Include="Implementation\Formatting\Indentation\WhitespaceExtensions.cs" />
<Compile Include="Implementation\GoToDefinition\AbstractGoToDefinitionService.cs" />
<Compile Include="Implementation\GoToDefinition\GoToDefinitionCommandHandler.cs" />
<Compile Include="Implementation\GoToDefinition\GoToDefinitionHelpers.cs" />
<Compile Include="Implementation\GoToDefinition\IGoToDefinitionService.cs" />
<Compile Include="GoToDefinition\AbstractGoToDefinitionService.cs" />
<Compile Include="GoToDefinition\GoToDefinitionCommandHandler.cs" />
<Compile Include="GoToDefinition\GoToDefinitionHelpers.cs" />
<Compile Include="GoToDefinition\IGoToDefinitionService.cs" />
<Compile Include="Implementation\Intellisense\Completion\FileSystem\CurrentWorkingDirectoryDiscoveryService.cs" />
<Compile Include="Implementation\Intellisense\Completion\FileSystem\FileSystemCompletionHelper.cs" />
<Compile Include="Implementation\Intellisense\Completion\FileSystem\ICurrentWorkingDirectoryDiscoveryService.cs" />
......@@ -782,4 +785,4 @@
<Folder Include="Extensibility\Navigation\" />
</ItemGroup>
<Import Project="..\..\..\build\Targets\Imports.targets" />
</Project>
</Project>
\ No newline at end of file
......@@ -3,85 +3,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
namespace Microsoft.CodeAnalysis.Editor.FindReferences
{
internal abstract partial class AbstractFindReferencesService :
ForegroundThreadAffinitizedObject, IFindReferencesService, IStreamingFindReferencesService
ForegroundThreadAffinitizedObject, IFindReferencesService
{
private readonly IEnumerable<IDefinitionsAndReferencesPresenter> _referenceSymbolPresenters;
private readonly IEnumerable<INavigableItemsPresenter> _navigableItemPresenters;
private readonly IEnumerable<IFindReferencesResultProvider> _externalReferencesProviders;
protected AbstractFindReferencesService(
IEnumerable<IDefinitionsAndReferencesPresenter> referenceSymbolPresenters,
IEnumerable<INavigableItemsPresenter> navigableItemPresenters,
IEnumerable<IFindReferencesResultProvider> externalReferencesProviders)
IEnumerable<INavigableItemsPresenter> navigableItemPresenters)
{
_referenceSymbolPresenters = referenceSymbolPresenters;
_navigableItemPresenters = navigableItemPresenters;
_externalReferencesProviders = externalReferencesProviders;
}
/// <summary>
/// Common helper for both the synchronous and streaming versions of FAR.
/// It returns the symbol we want to search for and the solution we should
/// be searching.
///
/// Note that the <see cref="Solution"/> returned may absolutely *not* be
/// the same as <code>document.Project.Solution</code>. This is because
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
private async Task<Tuple<ISymbol, Project>> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return null;
}
// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
// ISymbolMappingService to get a context for searching in the proper solution.
var mappingService = document.Project.Solution.Workspace.Services.GetService<ISymbolMappingService>();
var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}
return Tuple.Create(mapping.Symbol, mapping.Project);
}
/// <summary>
/// Finds references using the externally defined <see cref="IFindReferencesResultProvider"/>s.
/// </summary>
private async Task AddExternalReferencesAsync(Document document, int position, ArrayBuilder<INavigableItem> builder, CancellationToken cancellationToken)
{
// CONSIDER: Do the computation in parallel.
foreach (var provider in _externalReferencesProviders)
{
var references = await provider.FindReferencesAsync(document, position, cancellationToken).ConfigureAwait(false);
if (references != null)
{
builder.AddRange(references.WhereNotNull());
}
}
}
private async Task<Tuple<IEnumerable<ReferencedSymbol>, Solution>> FindReferencedSymbolsAsync(
......@@ -89,14 +34,15 @@ private async Task AddExternalReferencesAsync(Document document, int position, A
{
var cancellationToken = waitContext.CancellationToken;
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(document, position, cancellationToken).ConfigureAwait(false);
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}
var symbol = symbolAndProject.Item1;
var project = symbolAndProject.Item2;
var symbol = symbolAndProject?.symbol;
var project = symbolAndProject?.project;
var displayName = GetDisplayName(symbol);
......@@ -108,7 +54,7 @@ private async Task AddExternalReferencesAsync(Document document, int position, A
return Tuple.Create(result, project.Solution);
}
private static string GetDisplayName(ISymbol symbol)
public static string GetDisplayName(ISymbol symbol)
{
return symbol.IsConstructor() ? symbol.ContainingType.Name : symbol.Name;
}
......@@ -116,47 +62,11 @@ private static string GetDisplayName(ISymbol symbol)
public bool TryFindReferences(Document document, int position, IWaitContext waitContext)
{
var cancellationToken = waitContext.CancellationToken;
var workspace = document.Project.Solution.Workspace;
// First see if we have any external navigable item references.
// If so, we display the results as navigable items.
var succeeded = TryFindAndDisplayNavigableItemsReferencesAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
if (succeeded)
{
return true;
}
// Otherwise, fall back to displaying SymbolFinder based references.
var result = this.FindReferencedSymbolsAsync(document, position, waitContext).WaitAndGetResult(cancellationToken);
return TryDisplayReferences(result);
}
/// <summary>
/// Attempts to find and display navigable item references, including the references provided by external providers.
/// </summary>
/// <returns>False if there are no external references or display was not successful.</returns>
private async Task<bool> TryFindAndDisplayNavigableItemsReferencesAsync(Document document, int position, IWaitContext waitContext)
{
var foundReferences = false;
if (_externalReferencesProviders.Any())
{
var cancellationToken = waitContext.CancellationToken;
var builder = ArrayBuilder<INavigableItem>.GetInstance();
await AddExternalReferencesAsync(document, position, builder, cancellationToken).ConfigureAwait(false);
// TODO: Merging references from SymbolFinder and external providers might lead to duplicate or counter-intuitive results.
// TODO: For now, we avoid merging and just display the results either from SymbolFinder or the external result providers but not both.
if (builder.Count > 0 && TryDisplayReferences(builder))
{
foundReferences = true;
}
builder.Free();
}
return foundReferences;
}
private bool TryDisplayReferences(IEnumerable<INavigableItem> result)
{
if (result != null && result.Any())
......@@ -190,61 +100,5 @@ private bool TryDisplayReferences(Tuple<IEnumerable<ReferencedSymbol>, Solution>
return false;
}
public async Task FindReferencesAsync(
Document document, int position, FindReferencesContext context)
{
// NOTE: All ConFigureAwaits in this method need to pass 'true' so that
// we return to the caller's context. that's so the call to
// CallThirdPartyExtensionsAsync will happen on the UI thread. We need
// this to maintain the threading guarantee we had around that method
// from pre-Roslyn days.
var findReferencesProgress = await FindReferencesWorkerAsync(
document, position, context).ConfigureAwait(true);
if (findReferencesProgress == null)
{
return;
}
// After the FAR engine is done call into any third party extensions to see
// if they want to add results.
await findReferencesProgress.CallThirdPartyExtensionsAsync().ConfigureAwait(true);
}
private async Task<ProgressAdapter> FindReferencesWorkerAsync(
Document document, int position, FindReferencesContext context)
{
var cancellationToken = context.CancellationToken;
cancellationToken.ThrowIfCancellationRequested();
// Find the symbol we want to search and the solution we want to search in.
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}
var symbol = symbolAndProject.Item1;
var project = symbolAndProject.Item2;
var displayName = GetDisplayName(symbol);
context.SetSearchLabel(displayName);
var progressAdapter = new ProgressAdapter(project.Solution, context);
// Now call into the underlying FAR engine to find reference. The FAR
// engine will push results into the 'progress' instance passed into it.
// We'll take those results, massage them, and forward them along to the
// FindReferencesContext instance we were given.
await SymbolFinder.FindReferencesAsync(
SymbolAndProjectId.Create(symbol, project.Id),
project.Solution,
progressAdapter,
documents: null,
cancellationToken: cancellationToken).ConfigureAwait(false);
return progressAdapter;
}
}
}
}
\ No newline at end of file
......@@ -5,24 +5,26 @@
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
namespace Microsoft.CodeAnalysis.Editor.FindReferences
{
[ExportCommandHandler(PredefinedCommandHandlerNames.FindReferences, ContentTypeNames.RoslynContentType)]
internal class FindReferencesCommandHandler : ICommandHandler<FindReferencesCommandArgs>
{
private readonly IEnumerable<IDefinitionsAndReferencesPresenter> _synchronousPresenters;
private readonly IEnumerable<Lazy<IStreamingFindReferencesPresenter>> _streamingPresenters;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
private readonly IWaitIndicator _waitIndicator;
private readonly IAsynchronousOperationListener _asyncListener;
......@@ -31,7 +33,7 @@ internal class FindReferencesCommandHandler : ICommandHandler<FindReferencesComm
internal FindReferencesCommandHandler(
IWaitIndicator waitIndicator,
[ImportMany] IEnumerable<IDefinitionsAndReferencesPresenter> synchronousPresenters,
[ImportMany] IEnumerable<Lazy<IStreamingFindReferencesPresenter>> streamingPresenters,
[ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
[ImportMany] IEnumerable<Lazy<IAsynchronousOperationListener, FeatureMetadata>> asyncListeners)
{
Contract.ThrowIfNull(synchronousPresenters);
......@@ -72,8 +74,8 @@ public void ExecuteCommand(FindReferencesCommandArgs args, Action nextHandler)
private bool TryExecuteCommand(int caretPosition, Document document)
{
var streamingService = document.Project.LanguageServices.GetService<IStreamingFindReferencesService>();
var synchronousService = document.Project.LanguageServices.GetService<IFindReferencesService>();
var streamingService = document.GetLanguageService<IFindUsagesService>();
var synchronousService = document.GetLanguageService<IFindReferencesService>();
var streamingPresenter = GetStreamingPresenter();
......@@ -83,7 +85,7 @@ private bool TryExecuteCommand(int caretPosition, Document document)
var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingFindReferences, document.Project.Language);
if (streamingEnabled && streamingService != null && streamingPresenter != null)
{
StreamingFindReferences(document, streamingService, streamingPresenter, caretPosition);
StreamingFindReferences(document, caretPosition, streamingService, streamingPresenter);
return true;
}
......@@ -100,7 +102,7 @@ private bool TryExecuteCommand(int caretPosition, Document document)
return false;
}
private IStreamingFindReferencesPresenter GetStreamingPresenter()
private IStreamingFindUsagesPresenter GetStreamingPresenter()
{
try
{
......@@ -113,8 +115,9 @@ private IStreamingFindReferencesPresenter GetStreamingPresenter()
}
private async void StreamingFindReferences(
Document document, IStreamingFindReferencesService service,
IStreamingFindReferencesPresenter presenter, int caretPosition)
Document document, int caretPosition,
IFindUsagesService findUsagesService,
IStreamingFindUsagesPresenter presenter)
{
try
{
......@@ -122,8 +125,8 @@ private IStreamingFindReferencesPresenter GetStreamingPresenter()
{
// Let the presented know we're starging a search. It will give us back
// the context object that the FAR service will push results into.
var context = presenter.StartSearch();
await service.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
var context = presenter.StartSearch(EditorFeaturesResources.Find_References);
await findUsagesService.FindReferencesAsync(document, caretPosition, context).ConfigureAwait(false);
// Note: we don't need to put this in a finally. The only time we might not hit
// this is if cancellation or another error gets thrown. In the former case,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Host;
......@@ -15,13 +14,4 @@ internal interface IFindReferencesService : ILanguageService
/// <returns>True if finding references of the symbol at the provided position succeeds. False, otherwise.</returns>
bool TryFindReferences(Document document, int position, IWaitContext waitContext);
}
internal interface IStreamingFindReferencesService : ILanguageService
{
/// <summary>
/// Finds the references for the symbol at the specific position in the document,
/// pushing the results into the context instance.
/// </summary>
Task FindReferencesAsync(Document document, int position, FindReferencesContext context);
}
}
\ No newline at end of file
......@@ -4,22 +4,22 @@
using System.Collections.Concurrent;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal abstract partial class AbstractFindReferencesService
internal abstract partial class AbstractFindUsagesService
{
/// <summary>
/// Forwards IFindReferencesProgress calls to a FindRefrencesContext instance.
/// Forwards IFindReferencesProgress calls to an IFindUsagesContext instance.
/// </summary>
private class ProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFindReferencesProgress
{
private readonly Solution _solution;
private readonly FindReferencesContext _context;
private readonly IFindUsagesContext _context;
/// <summary>
/// We will hear about definition symbols many times while performing FAR. We'll
......@@ -36,7 +36,7 @@ private class ProgressAdapter : ForegroundThreadAffinitizedObject, IStreamingFin
private readonly Func<ISymbol, DefinitionItem> _definitionFactory;
public ProgressAdapter(Solution solution, FindReferencesContext context)
public ProgressAdapter(Solution solution, IFindUsagesContext context)
{
_solution = solution;
_context = context;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.GoToImplementation;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal abstract partial class AbstractFindUsagesService : IFindUsagesService
{
public async Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context)
{
var tuple = await FindUsagesHelpers.FindImplementationsAsync(
document, position, context.CancellationToken).ConfigureAwait(false);
if (tuple == null)
{
context.ReportMessage(EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret);
return;
}
var message = tuple.Value.message;
if (message != null)
{
context.ReportMessage(message);
return;
}
var project = tuple.Value.project;
foreach (var implementation in tuple.Value.implementations)
{
var definitionItem = implementation.ToDefinitionItem(project.Solution);
await context.OnDefinitionFoundAsync(definitionItem).ConfigureAwait(false);
}
}
public async Task FindReferencesAsync(
Document document, int position, IFindUsagesContext context)
{
// NOTE: All ConFigureAwaits in this method need to pass 'true' so that
// we return to the caller's context. that's so the call to
// CallThirdPartyExtensionsAsync will happen on the UI thread. We need
// this to maintain the threading guarantee we had around that method
// from pre-Roslyn days.
var findReferencesProgress = await FindReferencesWorkerAsync(
document, position, context).ConfigureAwait(true);
if (findReferencesProgress == null)
{
return;
}
// After the FAR engine is done call into any third party extensions to see
// if they want to add results.
await findReferencesProgress.CallThirdPartyExtensionsAsync().ConfigureAwait(true);
}
private async Task<ProgressAdapter> FindReferencesWorkerAsync(
Document document, int position, IFindUsagesContext context)
{
var cancellationToken = context.CancellationToken;
cancellationToken.ThrowIfCancellationRequested();
// Find the symbol we want to search and the solution we want to search in.
var symbolAndProject = await FindUsagesHelpers.GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}
var symbol = symbolAndProject?.symbol;
var project = symbolAndProject?.project;
var displayName = AbstractFindReferencesService.GetDisplayName(symbol);
context.SetSearchLabel(displayName);
var progressAdapter = new ProgressAdapter(project.Solution, context);
// Now call into the underlying FAR engine to find reference. The FAR
// engine will push results into the 'progress' instance passed into it.
// We'll take those results, massage them, and forward them along to the
// FindReferencesContext instance we were given.
await SymbolFinder.FindReferencesAsync(
SymbolAndProjectId.Create(symbol, project.Id),
project.Solution,
progressAdapter,
documents: null,
cancellationToken: cancellationToken).ConfigureAwait(false);
return progressAdapter;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindReferences;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor
namespace Microsoft.CodeAnalysis.FindUsages
{
internal abstract class FindReferencesContext
internal abstract class FindUsagesContext : IFindUsagesContext
{
public virtual CancellationToken CancellationToken { get; }
protected FindReferencesContext()
protected FindUsagesContext()
{
}
public virtual void ReportMessage(string message)
{
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal static class FindUsagesHelpers
{
/// <summary>
/// Common helper for both the synchronous and streaming versions of FAR.
/// It returns the symbol we want to search for and the solution we should
/// be searching.
///
/// Note that the <see cref="Solution"/> returned may absolutely *not* be
/// the same as <code>document.Project.Solution</code>. This is because
/// there may be symbol mapping involved (for example in Metadata-As-Source
/// scenarios).
/// </summary>
public static async Task<(ISymbol symbol, Project project)?> GetRelevantSymbolAndProjectAtPositionAsync(
Document document, int position, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
var symbol = await SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken: cancellationToken).ConfigureAwait(false);
if (symbol == null)
{
return null;
}
// If this document is not in the primary workspace, we may want to search for results
// in a solution different from the one we started in. Use the starting workspace's
// ISymbolMappingService to get a context for searching in the proper solution.
var mappingService = document.Project.Solution.Workspace.Services.GetService<ISymbolMappingService>();
var mapping = await mappingService.MapSymbolAsync(document, symbol, cancellationToken).ConfigureAwait(false);
if (mapping == null)
{
return null;
}
return (mapping.Symbol, mapping.Project);
}
public static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(Document document, int position, CancellationToken cancellationToken)
{
var symbolAndProject = await GetRelevantSymbolAndProjectAtPositionAsync(
document, position, cancellationToken).ConfigureAwait(false);
if (symbolAndProject == null)
{
return null;
}
return await FindImplementationsAsync(
symbolAndProject?.symbol, symbolAndProject?.project, cancellationToken).ConfigureAwait(false);
}
private static async Task<(ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, string message)?> FindImplementationsAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
var implementations = await FindImplementationsWorkerAsync(
symbol, project, cancellationToken).ConfigureAwait(false);
var filteredSymbols = implementations.WhereAsArray(
s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource));
return filteredSymbols.Length == 0
? (symbol, project, filteredSymbols, EditorFeaturesResources.The_symbol_has_no_implementations)
: (symbol, project, filteredSymbols, null);
}
private static async Task<ImmutableArray<ISymbol>> FindImplementationsWorkerAsync(
ISymbol symbol, Project project, CancellationToken cancellationToken)
{
var solution = project.Solution;
if (symbol.IsInterfaceType() || symbol.IsImplementableMember())
{
var implementations = await SymbolFinder.FindImplementationsAsync(
symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
// It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method
// in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
// is to just use a HashSet.
var implementationsAndOverrides = new HashSet<ISymbol>();
foreach (var implementation in implementations)
{
implementationsAndOverrides.Add(implementation);
// FindImplementationsAsync will only return the base virtual/abstract method, not that method and the overrides
// of the method. We should also include those.
if (implementation.IsOverridable())
{
var overrides = await SymbolFinder.FindOverridesAsync(
implementation, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
implementationsAndOverrides.AddRange(overrides);
}
}
return implementationsAndOverrides.ToImmutableArray();
}
else if ((symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
{
var derivedClasses = await SymbolFinder.FindDerivedClassesAsync(
(INamedTypeSymbol)symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = derivedClasses.Concat(symbol);
return implementations.ToImmutableArray();
}
else if (symbol.IsOverridable())
{
var overrides = await SymbolFinder.FindOverridesAsync(
symbol, solution, cancellationToken: cancellationToken).ConfigureAwait(false);
var implementations = overrides.Concat(symbol);
return implementations.ToImmutableArray();
}
else
{
// This is something boring like a regular method or type, so we'll just go there directly
return ImmutableArray.Create(symbol);
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Navigation;
namespace Microsoft.CodeAnalysis.Editor
namespace Microsoft.CodeAnalysis.FindUsages
{
internal interface INavigableDefinitionProvider
internal interface IFindUsagesContext
{
CancellationToken CancellationToken { get; }
/// <summary>
/// Find definitions for the symbol referenced or defined at the given position in the given document.
/// Report a message to be displayed to the user.
/// </summary>
Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(Document document, int position, CancellationToken cancellationToken);
void ReportMessage(string message);
/// <summary>
/// Find definitions for the given symbol in the context of the given project.
/// Set the title of the window that results are displayed in.
/// </summary>
Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(Project project, ISymbol symbol, CancellationToken cancellationToken);
void SetSearchLabel(string displayName);
Task OnDefinitionFoundAsync(DefinitionItem definition);
Task OnReferenceFoundAsync(SourceReferenceItem reference);
Task ReportProgressAsync(int current, int maximum);
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
internal interface IFindUsagesService : ILanguageService
{
/// <summary>
/// Finds the references for the symbol at the specific position in the document,
/// pushing the results into the context instance.
/// </summary>
Task FindReferencesAsync(Document document, int position, IFindUsagesContext context);
/// <summary>
/// Finds the implementationss for the symbol at the specific position in the document,
/// pushing the results into the context instance.
/// </summary>
Task FindImplementationsAsync(Document document, int position, IFindUsagesContext context);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindUsages;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.FindUsages
{
/// <summary>
/// Simple implementation of a <see cref="FindUsagesContext"/> that just aggregates the results
/// for consumers that just want the data once it is finally computed.
/// </summary>
internal class SimpleFindUsagesContext : FindUsagesContext
{
private readonly object _gate = new object();
private readonly ImmutableArray<DefinitionItem>.Builder _definitionItems =
ImmutableArray.CreateBuilder<DefinitionItem>();
private readonly ImmutableArray<SourceReferenceItem>.Builder _referenceItems =
ImmutableArray.CreateBuilder<SourceReferenceItem>();
public override CancellationToken CancellationToken { get; }
public SimpleFindUsagesContext(CancellationToken cancellationToken)
{
CancellationToken = cancellationToken;
}
public string Message { get; private set; }
public override void ReportMessage(string message)
=> Message = message;
public ImmutableArray<DefinitionItem> GetDefinitions()
{
lock (_gate)
{
return _definitionItems.ToImmutable();
}
}
public ImmutableArray<SourceReferenceItem> GetReferences()
{
lock (_gate)
{
return _referenceItems.ToImmutable();
}
}
public override Task OnDefinitionFoundAsync(DefinitionItem definition)
{
lock (_gate)
{
_definitionItems.Add(definition);
}
return SpecializedTasks.EmptyTask;
}
public override Task OnReferenceFoundAsync(SourceReferenceItem reference)
{
lock (_gate)
{
_referenceItems.Add(reference);
}
return SpecializedTasks.EmptyTask;
}
}
}
\ No newline at end of file
......@@ -12,19 +12,21 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
internal abstract class AbstractGoToDefinitionService : IGoToDefinitionService
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _presenters;
private readonly IEnumerable<Lazy<INavigableDefinitionProvider>> _externalDefinitionProviders;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
protected abstract ISymbol FindRelatedExplicitlyDeclaredSymbol(ISymbol symbol, Compilation compilation);
protected AbstractGoToDefinitionService(IEnumerable<Lazy<INavigableItemsPresenter>> presenters, IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders)
protected AbstractGoToDefinitionService(
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
_presenters = presenters;
_externalDefinitionProviders = externalDefinitionProviders;
_streamingPresenters = streamingPresenters;
}
private async Task<ISymbol> FindSymbolAsync(Document document, int position, CancellationToken cancellationToken)
......@@ -59,11 +61,6 @@ public async Task<IEnumerable<INavigableItem>> FindDefinitionsAsync(Document doc
var items = symbol != null
? NavigableItemFactory.GetItemsFromPreferredSourceLocations(document.Project.Solution, symbol, displayTaggedParts: null)
: null;
if (items == null || items.IsEmpty())
{
// Fallback to asking the navigation definition providers for navigable definition locations.
items = await GoToDefinitionHelpers.FindExternalDefinitionsAsync(document, position, _externalDefinitionProviders, cancellationToken).ConfigureAwait(false);
}
// realize the list here so that the consumer await'ing the result doesn't lazily cause
// them to be created on an inappropriate thread.
......@@ -74,21 +71,20 @@ public bool TryGoToDefinition(Document document, int position, CancellationToken
{
// First try to compute the referenced symbol and attempt to go to definition for the symbol.
var symbol = FindSymbolAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
if (symbol != null)
if (symbol == null)
{
var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);
return GoToDefinitionHelpers.TryGoToDefinition(symbol,
document.Project,
_externalDefinitionProviders,
_presenters,
thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
throwOnHiddenDefinition: true,
cancellationToken: cancellationToken);
return false;
}
// Otherwise, fallback to the external navigation definition providers.
return GoToDefinitionHelpers.TryExternalGoToDefinition(document, position, _externalDefinitionProviders, _presenters, cancellationToken);
var isThirdPartyNavigationAllowed = IsThirdPartyNavigationAllowed(symbol, position, document, cancellationToken);
return GoToDefinitionHelpers.TryGoToDefinition(symbol,
document.Project,
_presenters,
_streamingPresenters,
thirdPartyNavigationAllowed: isThirdPartyNavigationAllowed,
throwOnHiddenDefinition: true,
cancellationToken: cancellationToken);
}
private static bool IsThirdPartyNavigationAllowed(ISymbol symbolToNavigateTo, int caretPosition, Document document, CancellationToken cancellationToken)
......
......@@ -9,7 +9,7 @@
using Microsoft.CodeAnalysis.Text;
using Microsoft.VisualStudio.Text;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToDefinition,
ContentTypeNames.RoslynContentType)]
......
......@@ -12,16 +12,17 @@
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Options;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
namespace Microsoft.CodeAnalysis.Editor.GoToDefinition
{
internal static class GoToDefinitionHelpers
{
public static bool TryGoToDefinition(
ISymbol symbol,
Project project,
IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken,
bool thirdPartyNavigationAllowed = true,
bool throwOnHiddenDefinition = false)
......@@ -69,18 +70,8 @@ internal static class GoToDefinitionHelpers
var displayParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
var title = displayParts.JoinText();
if (!preferredSourceLocations.Any())
if (preferredSourceLocations.Length == 0)
{
// Attempt to find source locations from external definition providers.
if (thirdPartyNavigationAllowed && externalDefinitionProviders.Any())
{
var externalSourceDefinitions = FindExternalDefinitionsAsync(symbol, project, externalDefinitionProviders, cancellationToken).WaitAndGetResult(cancellationToken).ToImmutableArray();
if (externalSourceDefinitions.Length > 0)
{
return TryGoToDefinition(externalSourceDefinitions, title, options, presenters, throwOnHiddenDefinition);
}
}
// If there are no visible source locations, then tell the host about the symbol and
// allow it to navigate to it. This will either navigate to any non-visible source
// locations, or it can appropriately deal with metadata symbols for hosts that can go
......@@ -92,50 +83,12 @@ internal static class GoToDefinitionHelpers
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true),
cancellationToken: cancellationToken);
}
var navigableItems = preferredSourceLocations.Select(location =>
NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, location,
displayTaggedParts: null)).ToImmutableArray();
return TryGoToDefinition(navigableItems, title, options, presenters, throwOnHiddenDefinition);
}
private static bool TryGoToDefinition(
ImmutableArray<INavigableItem> navigableItems,
string title,
OptionSet options,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
bool throwOnHiddenDefinition)
{
Contract.ThrowIfNull(options);
// If we have a single location, then just navigate to it.
if (navigableItems.Length == 1 && navigableItems[0].Document != null)
else if (preferredSourceLocations.Length == 1)
{
var firstItem = navigableItems[0];
var workspace = firstItem.Document.Project.Solution.Workspace;
var navigationService = workspace.Services.GetService<IDocumentNavigationService>();
if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
{
return navigationService.TryNavigateToSpan(
workspace,
documentId: firstItem.Document.Id,
textSpan: firstItem.SourceSpan,
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
}
else
{
if (throwOnHiddenDefinition)
{
const int E_FAIL = -2147467259;
throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
}
else
{
return false;
}
}
var item = NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, preferredSourceLocations[0],
displayTaggedParts: null);
return TryGoToSingleLocation(item, options, throwOnHiddenDefinition);
}
else
{
......@@ -143,65 +96,98 @@ internal static class GoToDefinitionHelpers
// will simply display the results to the user and allow them to choose where to
// go.
if (presenters.Any())
{
presenters.First().Value.DisplayResult(title, navigableItems);
return true;
}
return false;
return TryPresentInFindUsagesPresenter(solution, symbol, streamingPresenters, cancellationToken) ||
TryPresentInNavigableItemsPresenter(solution, symbol, presenters, title, preferredSourceLocations);
}
}
public static async Task<IEnumerable<INavigableItem>> FindExternalDefinitionsAsync(Document document, int position, IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders, CancellationToken cancellationToken)
private static bool TryPresentInFindUsagesPresenter(
Solution solution, ISymbol symbol,
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters,
CancellationToken cancellationToken)
{
foreach (var definitionProvider in externalDefinitionProviders)
var presenter = GetFindUsagesPresenter(streamingPresenters);
if (presenter == null)
{
var definitions = await definitionProvider.Value.FindDefinitionsAsync(document, position, cancellationToken).ConfigureAwait(false);
if (definitions != null && definitions.Any())
{
var preferredDefinitions = NavigableItemFactory.GetPreferredNavigableItems(document.Project.Solution, definitions);
if (preferredDefinitions.Any())
{
return preferredDefinitions;
}
}
return false;
}
return SpecializedCollections.EmptyEnumerable<INavigableItem>();
var definition = symbol.ToDefinitionItem(solution);
var context = presenter.StartSearch(EditorFeaturesResources.Go_to_Definition);
try
{
context.OnDefinitionFoundAsync(definition).Wait(cancellationToken);
}
finally
{
context.OnCompletedAsync().Wait(cancellationToken);
}
return true;
}
public static async Task<IEnumerable<INavigableItem>> FindExternalDefinitionsAsync(ISymbol symbol, Project project, IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders, CancellationToken cancellationToken)
private static IStreamingFindUsagesPresenter GetFindUsagesPresenter(
IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
foreach (var definitionProvider in externalDefinitionProviders)
try
{
var definitions = await definitionProvider.Value.FindDefinitionsAsync(project, symbol, cancellationToken).ConfigureAwait(false);
if (definitions != null && definitions.Any())
{
var preferredDefinitions = NavigableItemFactory.GetPreferredNavigableItems(project.Solution, definitions);
if (preferredDefinitions.Any())
{
return preferredDefinitions;
}
}
return streamingPresenters.FirstOrDefault()?.Value;
}
catch
{
return null;
}
return SpecializedCollections.EmptyEnumerable<INavigableItem>();
}
public static bool TryExternalGoToDefinition(Document document, int position, IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders, IEnumerable<Lazy<INavigableItemsPresenter>> presenters, CancellationToken cancellationToken)
private static bool TryPresentInNavigableItemsPresenter(
Solution solution, ISymbol symbol,
IEnumerable<Lazy<INavigableItemsPresenter>> presenters,
string title, Location[] locations)
{
var externalDefinitions = FindExternalDefinitionsAsync(document, position, externalDefinitionProviders, cancellationToken).WaitAndGetResult(cancellationToken);
if (externalDefinitions != null && externalDefinitions.Any())
var presenter = presenters.FirstOrDefault();
if (presenter != null)
{
var itemsArray = externalDefinitions.ToImmutableArrayOrEmpty();
var title = itemsArray[0].DisplayTaggedParts.JoinText();
return TryGoToDefinition(externalDefinitions.ToImmutableArrayOrEmpty(), title, document.Project.Solution.Workspace.Options, presenters, throwOnHiddenDefinition: true);
var navigableItems = locations.Select(location =>
NavigableItemFactory.GetItemFromSymbolLocation(
solution, symbol, location,
displayTaggedParts: null)).ToImmutableArray();
presenter.Value.DisplayResult(title, navigableItems);
return true;
}
return false;
}
private static bool TryGoToSingleLocation(
INavigableItem navigableItem, OptionSet options, bool throwOnHiddenDefinition)
{
var firstItem = navigableItem;
var workspace = firstItem.Document.Project.Solution.Workspace;
var navigationService = workspace.Services.GetService<IDocumentNavigationService>();
if (navigationService.CanNavigateToSpan(workspace, firstItem.Document.Id, firstItem.SourceSpan))
{
return navigationService.TryNavigateToSpan(
workspace,
documentId: firstItem.Document.Id,
textSpan: firstItem.SourceSpan,
options: options.WithChangedOption(NavigationOptions.PreferProvisionalTab, true));
}
else
{
if (throwOnHiddenDefinition)
{
const int E_FAIL = -2147467259;
throw new COMException(EditorFeaturesResources.The_definition_of_the_object_is_hidden, E_FAIL);
}
else
{
return false;
}
}
}
private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
{
var symbolNavigationService = solution.Workspace.Services.GetService<ISymbolNavigationService>();
......@@ -210,4 +196,4 @@ private static bool TryThirdPartyNavigation(ISymbol symbol, Solution solution)
return symbolNavigationService.TrySymbolNavigationNotify(symbol, solution);
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.GoToDefinition;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
{
internal abstract class AbstractGoToImplementationService : IGoToImplementationService
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemPresenters;
public AbstractGoToImplementationService(
IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters)
{
_navigableItemPresenters = navigableItemPresenters;
}
public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
{
var result = FindUsagesHelpers.FindImplementationsAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
if (result == null)
{
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
if (result.Value.message != null)
{
message = result.Value.message;
return false;
}
return TryGoToImplementations(
result.Value.symbol, result.Value.project,
result.Value.implementations, cancellationToken, out message);
}
private bool TryGoToImplementations(
ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, CancellationToken cancellationToken, out string message)
{
if (implementations.Length == 0)
{
message = EditorFeaturesResources.The_symbol_has_no_implementations;
return false;
}
else if (implementations.Length == 1)
{
GoToDefinitionHelpers.TryGoToDefinition(
implementations.Single(), project, _navigableItemPresenters,
SpecializedCollections.EmptyEnumerable<Lazy<IStreamingFindUsagesPresenter>>(),
cancellationToken);
message = null;
return true;
}
else
{
return TryPresentInNavigableItemsPresenter(symbol, project, implementations, out message);
}
}
private bool TryPresentInNavigableItemsPresenter(
ISymbol symbol, Project project, ImmutableArray<ISymbol> implementations, out string message)
{
// We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
var navigableItems = implementations.SelectMany(
implementation => CreateItemsForImplementation(implementation, project.Solution));
var presenter = _navigableItemPresenters.First();
var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(project, symbol);
presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
message = null;
return true;
}
private static IEnumerable<INavigableItem> CreateItemsForImplementation(ISymbol implementation, Solution solution)
{
var symbolDisplayService = solution.Workspace.Services.GetLanguageServices(implementation.Language).GetRequiredService<ISymbolDisplayService>();
return NavigableItemFactory.GetItemsFromPreferredSourceLocations(
solution,
implementation,
displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText());
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.FindUsages;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Editor.Shared.Options;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Editor.GoToImplementation
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
ContentTypeNames.RoslynContentType)]
internal partial class GoToImplementationCommandHandler : ICommandHandler<GoToImplementationCommandArgs>
{
private readonly IWaitIndicator _waitIndicator;
private readonly IEnumerable<Lazy<IStreamingFindUsagesPresenter>> _streamingPresenters;
[ImportingConstructor]
public GoToImplementationCommandHandler(
IWaitIndicator waitIndicator,
[ImportMany] IEnumerable<Lazy<IStreamingFindUsagesPresenter>> streamingPresenters)
{
_waitIndicator = waitIndicator;
_streamingPresenters = streamingPresenters;
}
public CommandState GetCommandState(GoToImplementationCommandArgs args, Func<CommandState> nextHandler)
{
// Because this is expensive to compute, we just always say yes
return CommandState.Available;
}
public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
{
var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
if (caret.HasValue)
{
var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
ExecuteCommand(document, caret.Value);
return;
}
}
nextHandler();
}
private void ExecuteCommand(Document document, int caretPosition)
{
var streamingService = document.GetLanguageService<IFindUsagesService>();
var synchronousService = document.GetLanguageService<IGoToImplementationService>();
var streamingPresenter = GetStreamingPresenter();
// See if we're running on a host that can provide streaming results.
// We'll both need a FAR service that can stream results to us, and
// a presenter that can accept streamed results.
var streamingEnabled = document.Project.Solution.Workspace.Options.GetOption(FeatureOnOffOptions.StreamingGoToImplementation, document.Project.Language);
var canUseStreamingWindow = streamingEnabled && streamingService != null && streamingPresenter != null;
var canUseSynchronousWindow = synchronousService != null;
if (canUseStreamingWindow || canUseSynchronousWindow)
{
// We have all the cheap stuff, so let's do expensive stuff now
string messageToShow = null;
_waitIndicator.Wait(
EditorFeaturesResources.Go_To_Implementation,
EditorFeaturesResources.Locating_implementations,
allowCancel: true,
action: context =>
{
if (canUseStreamingWindow)
{
StreamingGoToImplementation(
document, caretPosition,
streamingService, streamingPresenter,
context.CancellationToken, out messageToShow);
}
else
{
synchronousService.TryGoToImplementation(
document, caretPosition, context.CancellationToken, out messageToShow);
}
});
if (messageToShow != null)
{
var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
notificationService.SendNotification(messageToShow,
title: EditorFeaturesResources.Go_To_Implementation,
severity: NotificationSeverity.Information);
}
}
}
private void StreamingGoToImplementation(
Document document, int caretPosition,
IFindUsagesService findUsagesService,
IStreamingFindUsagesPresenter streamingPresenter,
CancellationToken cancellationToken,
out string messageToShow)
{
// We create our own context object, simply to capture all the definitions reported by
// the individual IFindUsagesService. Once we get the results back we'll then decide
// what to do with them. If we get only a single result back, then we'll just go
// directly to it. Otherwise, we'll present the results in the IStreamingFindUsagesPresenter.
var goToImplContext = new SimpleFindUsagesContext(cancellationToken);
findUsagesService.FindImplementationsAsync(document, caretPosition, goToImplContext).Wait(cancellationToken);
// If finding implementations reported a message, then just stop and show that
// message to the user.
messageToShow = goToImplContext.Message;
if (messageToShow != null)
{
return;
}
var definitionItems = goToImplContext.GetDefinitions();
streamingPresenter.NavigateToOrPresentItemsAsync(
EditorFeaturesResources.Go_To_Implementation, definitionItems).Wait(cancellationToken);
}
private IStreamingFindUsagesPresenter GetStreamingPresenter()
{
try
{
return _streamingPresenters.FirstOrDefault()?.Value;
}
catch
{
return null;
}
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.Host
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.Host
{
/// <summary>
/// API for hosts to provide if they can present FindReferences results in a streaming manner.
/// API for hosts to provide if they can present FindUsages results in a streaming manner.
/// i.e. if they support showing results as they are found instead of after all of the results
/// are found.
/// </summary>
internal interface IStreamingFindReferencesPresenter
internal interface IStreamingFindUsagesPresenter
{
/// <summary>
/// Tells the presenter that a search is starting. The returned <see cref="FindReferencesContext"/>
/// Tells the presenter that a search is starting. The returned <see cref="FindUsagesContext"/>
/// is used to push information about the search into. i.e. when a reference is found
/// <see cref="FindReferencesContext.OnReferenceFoundAsync"/> should be called. When the
/// search completes <see cref="FindReferencesContext.OnCompletedAsync"/> should be called.
/// <see cref="FindUsagesContext.OnReferenceFoundAsync"/> should be called. When the
/// search completes <see cref="FindUsagesContext.OnCompletedAsync"/> should be called.
/// etc. etc.
/// </summary>
FindReferencesContext StartSearch();
FindUsagesContext StartSearch(string title);
}
internal static class IStreamingFindUsagesPresenterExtensions
{
/// <summary>
/// If there's only a single item, navigates to it. Otherwise, presents all the
/// items to the user.
/// </summary>
public static async Task NavigateToOrPresentItemsAsync(
this IStreamingFindUsagesPresenter presenter,
string title, ImmutableArray<DefinitionItem> items)
{
// Ignore any definitions that we can't navigate to.
var definitions = items.WhereAsArray(d => d.CanNavigateTo());
// See if there's a third party external item we can navigate to. If so, defer
// to that item and finish.
var externalItems = definitions.WhereAsArray(d => d.IsExternal);
foreach (var item in externalItems)
{
if (item.TryNavigateTo())
{
return;
}
}
var nonExternalItems = definitions.WhereAsArray(d => !d.IsExternal);
if (nonExternalItems.Length == 0)
{
return;
}
if (nonExternalItems.Length == 1 &&
nonExternalItems[0].SourceSpans.Length <= 1)
{
// There was only one location to navigate to. Just directly go to that location.
nonExternalItems[0].TryNavigateTo();
return;
}
// We have multiple definitions, or we have definitions with multiple locations.
// Present this to the user so they can decide where they want to go to.
var context = presenter.StartSearch(title);
foreach (var definition in nonExternalItems)
{
await context.OnDefinitionFoundAsync(definition).ConfigureAwait(false);
}
// Note: we don't need to put this in a finally. The only time we might not hit
// this is if cancellation or another error gets thrown. In the former case,
// that means that a new search has started. We don't care about telling the
// context it has completed. In the latter case somethign wrong has happened
// and we don't want to run any more code code in this particular context.
await context.OnCompletedAsync().ConfigureAwait(false);
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Navigation;
namespace Microsoft.CodeAnalysis.Editor
{
internal interface IFindReferencesResultProvider
{
/// <summary>
/// Compute navigable reference items for the symbol referenced or defined at the given position in the given document.
/// </summary>
Task<IEnumerable<INavigableItem>> FindReferencesAsync(Document document, int position, CancellationToken cancellationToken);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.SymbolMapping;
using Microsoft.CodeAnalysis.FindSymbols;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Navigation;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
{
internal abstract class AbstractGoToImplementationService : IGoToImplementationService
{
private readonly IEnumerable<Lazy<INavigableItemsPresenter>> _navigableItemPresenters;
private readonly IEnumerable<Lazy<INavigableDefinitionProvider>> _externalDefinitionProviders;
public AbstractGoToImplementationService(
IEnumerable<Lazy<INavigableItemsPresenter>> navigableItemPresenters,
IEnumerable<Lazy<INavigableDefinitionProvider>> externalDefinitionProviders)
{
_navigableItemPresenters = navigableItemPresenters;
_externalDefinitionProviders = externalDefinitionProviders;
}
public bool TryGoToImplementation(Document document, int position, CancellationToken cancellationToken, out string message)
{
var symbol = SymbolFinder.FindSymbolAtPositionAsync(document, position, cancellationToken).WaitAndGetResult(cancellationToken);
if (symbol != null)
{
// Map the symbol if necessary back to the originating workspace if we're invoking from something
// like metadata as source
var mappingService = document.Project.Solution.Workspace.Services.GetRequiredService<ISymbolMappingService>();
var mapping = mappingService.MapSymbolAsync(document, symbol, cancellationToken).WaitAndGetResult(cancellationToken);
if (mapping != null)
{
return TryGoToImplementationOnMappedSymbol(mapping, cancellationToken, out message);
}
}
else
{
return TryExternalGotoDefinition(document, position, cancellationToken, out message);
}
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
private bool TryGoToImplementationOnMappedSymbol(SymbolMappingResult mapping, CancellationToken cancellationToken, out string message)
{
if (mapping.Symbol.IsInterfaceType() || mapping.Symbol.IsImplementableMember())
{
var implementations =
SymbolFinder.FindImplementationsAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
.WaitAndGetResult(cancellationToken);
// It's important we use a HashSet here -- we may have cases in an inheritence hierarchy where more than one method
// in an overrides chain implements the same interface method, and we want to duplicate those. The easiest way to do it
// is to just use a HashSet.
var implementationsAndOverrides = new HashSet<ISymbol>();
foreach (var implementation in implementations)
{
implementationsAndOverrides.Add(implementation);
// FindImplementationsAsync will only return the base virtual/abstract method, not that method and the overrides
// of the method. We should also include those.
if (implementation.IsOverridable())
{
implementationsAndOverrides.AddRange(
SymbolFinder.FindOverridesAsync(implementation, mapping.Solution, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken));
}
}
return TryGoToImplementations(implementationsAndOverrides, mapping, cancellationToken, out message);
}
else if ((mapping.Symbol as INamedTypeSymbol)?.TypeKind == TypeKind.Class)
{
var implementations =
SymbolFinder.FindDerivedClassesAsync((INamedTypeSymbol)mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
.WaitAndGetResult(cancellationToken)
.Concat(mapping.Symbol);
return TryGoToImplementations(implementations, mapping, cancellationToken, out message);
}
else if (mapping.Symbol.IsOverridable())
{
var implementations =
SymbolFinder.FindOverridesAsync(mapping.Symbol, mapping.Solution, cancellationToken: cancellationToken)
.WaitAndGetResult(cancellationToken)
.Concat(mapping.Symbol);
return TryGoToImplementations(implementations, mapping, cancellationToken, out message);
}
else
{
// This is something boring like a regular method or type, so we'll just go there directly
if (GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(mapping.Symbol, mapping.Project, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken))
{
message = null;
return true;
}
else
{
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
}
}
private bool TryGoToImplementations(IEnumerable<ISymbol> candidateImplementations, SymbolMappingResult mapping, CancellationToken cancellationToken, out string message)
{
var implementations = candidateImplementations
.Where(s => !s.IsAbstract && s.Locations.Any(l => l.IsInSource))
.ToList();
if (implementations.Count == 0)
{
message = EditorFeaturesResources.The_symbol_has_no_implementations;
return false;
}
else if (implementations.Count == 1)
{
GoToDefinition.GoToDefinitionHelpers.TryGoToDefinition(implementations.Single(), mapping.Project, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken);
message = null;
return true;
}
else
{
// We have multiple symbols, so we'll build a list of all preferred locations for all the symbols
var navigableItems = implementations.SelectMany(
implementation => CreateItemsForImplementation(implementation, mapping.Solution));
var presenter = _navigableItemPresenters.First();
var taggedParts = NavigableItemFactory.GetSymbolDisplayTaggedParts(mapping.Project, mapping.Symbol);
presenter.Value.DisplayResult(taggedParts.JoinText(), navigableItems);
message = null;
return true;
}
}
private static IEnumerable<INavigableItem> CreateItemsForImplementation(ISymbol implementation, Solution solution)
{
var symbolDisplayService = solution.Workspace.Services.GetLanguageServices(implementation.Language).GetRequiredService<ISymbolDisplayService>();
return NavigableItemFactory.GetItemsFromPreferredSourceLocations(
solution,
implementation,
displayTaggedParts: symbolDisplayService.ToDisplayParts(implementation).ToTaggedText());
}
private bool TryExternalGotoDefinition(Document document, int position, CancellationToken cancellationToken, out string message)
{
if (GoToDefinition.GoToDefinitionHelpers.TryExternalGoToDefinition(document, position, _externalDefinitionProviders, _navigableItemPresenters, cancellationToken))
{
message = null;
return true;
}
else
{
message = EditorFeaturesResources.Cannot_navigate_to_the_symbol_under_the_caret;
return false;
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.ComponentModel.Composition;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Shared.Extensions;
using Microsoft.CodeAnalysis.Notification;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
{
[ExportCommandHandler(PredefinedCommandHandlerNames.GoToImplementation,
ContentTypeNames.RoslynContentType)]
internal sealed class GoToImplementationCommandHandler : ICommandHandler<GoToImplementationCommandArgs>
{
private readonly IWaitIndicator _waitIndicator;
[ImportingConstructor]
public GoToImplementationCommandHandler(
IWaitIndicator waitIndicator)
{
_waitIndicator = waitIndicator;
}
public CommandState GetCommandState(GoToImplementationCommandArgs args, Func<CommandState> nextHandler)
{
// Because this is expensive to compute, we just always say yes
return CommandState.Available;
}
public void ExecuteCommand(GoToImplementationCommandArgs args, Action nextHandler)
{
var caret = args.TextView.GetCaretPoint(args.SubjectBuffer);
if (caret.HasValue)
{
var document = args.SubjectBuffer.CurrentSnapshot.GetOpenDocumentInCurrentContextWithChanges();
if (document != null)
{
var service = document.Project.LanguageServices.GetService<IGoToImplementationService>();
if (service != null)
{
// We have all the cheap stuff, so let's do expensive stuff now
string messageToShow = null;
bool succeeded = false;
_waitIndicator.Wait(
EditorFeaturesResources.Go_To_Implementation,
EditorFeaturesResources.Locating_implementations,
allowCancel: true,
action: context => succeeded = service.TryGoToImplementation(document, caret.Value, context.CancellationToken, out messageToShow));
if (messageToShow != null)
{
var notificationService = document.Project.Solution.Workspace.Services.GetService<INotificationService>();
notificationService.SendNotification(messageToShow,
title: EditorFeaturesResources.Go_To_Implementation,
severity: NotificationSeverity.Information);
}
}
return;
}
}
nextHandler();
}
}
}
......@@ -87,9 +87,15 @@ internal static class FeatureOnOffOptions
/// implemented this feature yet.
/// </summary>
[ExportOption]
public static readonly PerLanguageOption<bool> RefactoringVerification = new PerLanguageOption<bool>(nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false);
public static readonly PerLanguageOption<bool> RefactoringVerification = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(RefactoringVerification), defaultValue: false);
[ExportOption]
public static readonly PerLanguageOption<bool> StreamingFindReferences = new PerLanguageOption<bool>(nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true);
public static readonly PerLanguageOption<bool> StreamingFindReferences = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(StreamingFindReferences), defaultValue: true);
[ExportOption]
public static readonly PerLanguageOption<bool> StreamingGoToImplementation = new PerLanguageOption<bool>(
nameof(FeatureOnOffOptions), nameof(StreamingGoToImplementation), defaultValue: true);
}
}
}
\ No newline at end of file
......@@ -249,6 +249,7 @@
<Compile Include="Utilities\SymbolEquivalenceComparerTests.cs" />
<Compile Include="Workspaces\ProjectCacheHostServiceFactoryTests.cs" />
<Compile Include="Workspaces\TextFactoryTests.cs" />
<Compile Include="Workspaces\WorkspaceTests.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
......
......@@ -4,11 +4,11 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Editor.Commands;
using Microsoft.CodeAnalysis.Editor.FindReferences;
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.Editor.Implementation.FindReferences;
using Microsoft.CodeAnalysis.Editor.UnitTests.Utilities;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindUsages;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.VisualStudio.Text;
using Roslyn.Test.Utilities;
......@@ -29,7 +29,7 @@ public async Task TestFindReferencesSynchronousCall()
var handler = new FindReferencesCommandHandler(
TestWaitIndicator.Default,
SpecializedCollections.SingletonEnumerable(findReferencesPresenter),
SpecializedCollections.EmptyEnumerable<Lazy<IStreamingFindReferencesPresenter>>(),
SpecializedCollections.EmptyEnumerable<Lazy<IStreamingFindUsagesPresenter>>(),
workspace.ExportProvider.GetExports<IAsynchronousOperationListener, FeatureMetadata>());
var textView = workspace.Documents[0].GetTextView();
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Editor.Host;
using Microsoft.CodeAnalysis.FindReferences;
using Microsoft.CodeAnalysis.FindUsages;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.Implementation.Workspaces;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Test.Utilities;
using Roslyn.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
{
public class WorkspaceTests
{
[Fact]
public void TestDefaultCompositionIncludesFeaturesLayer()
{
var ws = new AdhocWorkspace();
var csservice = ws.Services.GetLanguageServices(LanguageNames.CSharp).GetService<Microsoft.CodeAnalysis.Completion.CompletionService>();
Assert.NotNull(csservice);
var vbservice = ws.Services.GetLanguageServices(LanguageNames.VisualBasic).GetService<Microsoft.CodeAnalysis.Completion.CompletionService>();
Assert.NotNull(vbservice);
}
}
}
\ No newline at end of file
......@@ -4,11 +4,12 @@ Imports System.Collections.Immutable
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.FindReferences
Imports Microsoft.CodeAnalysis.FindUsages
Imports Microsoft.CodeAnalysis.FindSymbols
Imports Microsoft.CodeAnalysis.Text
Imports Roslyn.Utilities
Imports Xunit.Abstractions
Imports Microsoft.CodeAnalysis.Editor.FindUsages
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Partial Public Class FindReferencesTests
......@@ -38,7 +39,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
Dim startDocument = workspace.CurrentSolution.GetDocument(cursorDocument.Id)
Assert.NotNull(startDocument)
Dim findRefsService = startDocument.GetLanguageService(Of IStreamingFindReferencesService)
Dim findRefsService = startDocument.GetLanguageService(Of IFindUsagesService)
Dim context = New TestContext()
Await findRefsService.FindReferencesAsync(startDocument, cursorPosition, context)
......@@ -109,7 +110,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.FindReferences
End Structure
Private Class TestContext
Inherits FindReferencesContext
Inherits FindUsagesContext
Private ReadOnly gate As Object = New Object()
......
......@@ -3,7 +3,7 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis.Editor.CSharp.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
......
......@@ -11,10 +11,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToDefinition
Public Class GoToDefinitionTests
Private Function TestAsync(workspaceDefinition As XElement, Optional expectedResult As Boolean = True) As Tasks.Task
Return GoToTestHelpers.TestAsync(workspaceDefinition, expectedResult,
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim goToDefService = If(document.Project.Language = LanguageNames.CSharp,
DirectCast(New CSharpGoToDefinitionService(presenters, externalDefinitionProviders), IGoToDefinitionService),
New VisualBasicGoToDefinitionService(presenters, externalDefinitionProviders))
DirectCast(New CSharpGoToDefinitionService(presenters, {}), IGoToDefinitionService),
New VisualBasicGoToDefinitionService(presenters, {}))
Return goToDefService.TryGoToDefinition(document, cursorPosition, CancellationToken.None)
End Function)
......
......@@ -11,10 +11,10 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.GoToImplementation
Public Class GoToImplementationTests
Private Function TestAsync(workspaceDefinition As XElement, Optional shouldSucceed As Boolean = True) As Tasks.Task
Return GoToTestHelpers.TestAsync(workspaceDefinition, shouldSucceed,
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
Function(document As Document, cursorPosition As Integer, presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
Dim service = If(document.Project.Language = LanguageNames.CSharp,
DirectCast(New CSharpGoToImplementationService(presenters, externalDefinitionProviders), IGoToImplementationService),
New VisualBasicGoToImplementationService(presenters, externalDefinitionProviders))
DirectCast(New CSharpGoToImplementationService(presenters), IGoToImplementationService),
New VisualBasicGoToImplementationService(presenters))
Dim message As String = Nothing
Return service.TryGoToImplementation(document, cursorPosition, CancellationToken.None, message)
......
......@@ -530,10 +530,13 @@ protected virtual IList<CodeAction> MassageActions(IList<CodeAction> actions)
protected static IList<CodeAction> FlattenActions(IEnumerable<CodeAction> codeActions)
{
return codeActions?.SelectMany(a => a.NestedCodeActions.Length > 0
? a.NestedCodeActions.ToArray()
? a.NestedCodeActions.ToArray()
: new[] { a }).ToList();
}
protected (OptionKey, object) SingleOption(Option<bool> option, bool enabled)
=> (new OptionKey(option), enabled);
protected (OptionKey, object) SingleOption(Option<CodeStyleOption<bool>> option, bool enabled, NotificationOption notification)
=> SingleOption(option, new CodeStyleOption<bool>(enabled, notification));
......
......@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Public ReadOnly ExportProvider As ExportProvider = MinimalTestExportProvider.CreateExportProvider(Catalog)
Public Async Function TestAsync(workspaceDefinition As XElement, expectedResult As Boolean, executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of INavigableItemsPresenter)), IEnumerable(Of Lazy(Of INavigableDefinitionProvider)), Boolean)) As System.Threading.Tasks.Task
Public Async Function TestAsync(workspaceDefinition As XElement, expectedResult As Boolean, executeOnDocument As Func(Of Document, Integer, IEnumerable(Of Lazy(Of INavigableItemsPresenter)), Boolean)) As System.Threading.Tasks.Task
Using workspace = Await TestWorkspace.CreateAsync(workspaceDefinition, exportProvider:=ExportProvider)
Dim solution = workspace.CurrentSolution
Dim cursorDocument = workspace.Documents.First(Function(d) d.CursorPosition.HasValue)
......@@ -41,7 +41,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities.GoToHelpers
Dim presenter = New MockNavigableItemsPresenter(Sub(i) items = i)
Dim presenters = {New Lazy(Of INavigableItemsPresenter)(Function() presenter)}
Dim actualResult = executeOnDocument(document, cursorPosition, presenters, {})
Dim actualResult = executeOnDocument(document, cursorPosition, presenters)
Assert.Equal(expectedResult, actualResult)
......
......@@ -100,6 +100,7 @@
<Compile Include="ExtractInterface\ExtractInterfaceCommandHandler.vb" />
<Compile Include="ExtractMethod\ExtractMethodCommandHandler.vb" />
<Compile Include="FindReferences\VisualBasicFindReferencesService.vb" />
<Compile Include="FindUsages\VisualBasicFindUsagesService.vb" />
<Compile Include="Formatting\Indentation\SmartTokenFormatter.vb" />
<Compile Include="Formatting\Indentation\SmartTokenFormatterCommandHandler.vb" />
<Compile Include="Formatting\Indentation\SpecialFormattingOperation.vb" />
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
Imports Microsoft.CodeAnalysis.Editor.FindReferences
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.FindReferences
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindReferences
......@@ -12,21 +12,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindReferences
<ImportingConstructor>
Protected Sub New(<ImportMany> referencedSymbolsPresenters As IEnumerable(Of IDefinitionsAndReferencesPresenter),
<ImportMany> navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter),
<ImportMany> externalReferencesProviders As IEnumerable(Of IFindReferencesResultProvider))
MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
End Sub
End Class
<ExportLanguageService(GetType(IStreamingFindReferencesService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicStreamingFindReferencesService
Inherits AbstractFindReferencesService
<ImportingConstructor>
Protected Sub New(<ImportMany> referencedSymbolsPresenters As IEnumerable(Of IDefinitionsAndReferencesPresenter),
<ImportMany> navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter),
<ImportMany> externalReferencesProviders As IEnumerable(Of IFindReferencesResultProvider))
MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters, externalReferencesProviders)
<ImportMany> navigableItemsPresenters As IEnumerable(Of INavigableItemsPresenter))
MyBase.New(referencedSymbolsPresenters, navigableItemsPresenters)
End Sub
End Class
End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
Imports Microsoft.CodeAnalysis.Editor.FindUsages
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.FindUsages
<ExportLanguageService(GetType(IFindUsagesService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicFindUsagesService
Inherits AbstractFindUsagesService
End Class
End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
Imports Microsoft.CodeAnalysis.Editor.GoToDefinition
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToDefinition
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.VisualBasic.Utilities
......@@ -12,12 +12,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToDefinition
Inherits AbstractGoToDefinitionService
<ImportingConstructor>
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), <ImportMany> externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
MyBase.New(presenters, externalDefinitionProviders)
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)),
<ImportMany> streamingPresenters As IEnumerable(Of Lazy(Of IStreamingFindUsagesPresenter)))
MyBase.New(presenters, streamingPresenters)
End Sub
Protected Overrides Function FindRelatedExplicitlyDeclaredSymbol(symbol As ISymbol, compilation As Compilation) As ISymbol
Return symbol.FindRelatedExplicitlyDeclaredSymbol(compilation)
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Composition
Imports Microsoft.CodeAnalysis.Editor.GoToImplementation
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.Implementation.GoToImplementation
Imports Microsoft.CodeAnalysis.Host.Mef
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToImplementation
......@@ -11,8 +11,8 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.GoToImplementation
Inherits AbstractGoToImplementationService
<ImportingConstructor>
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)), <ImportMany> externalDefinitionProviders As IEnumerable(Of Lazy(Of INavigableDefinitionProvider)))
MyBase.New(presenters, externalDefinitionProviders)
Public Sub New(<ImportMany> presenters As IEnumerable(Of Lazy(Of INavigableItemsPresenter)))
MyBase.New(presenters)
End Sub
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -8,12 +8,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Completion
Public Class CompletionServiceTests
<Fact, Trait(Traits.Feature, Traits.Features.Completion)>
Public Sub AcquireCompletionService()
Dim hostServices = MefHostServices.Create(
MefHostServices.DefaultAssemblies.Concat({
GetType(CompletionService).Assembly,
GetType(VisualBasicCompletionService).Assembly}))
Dim workspace = New AdhocWorkspace(hostServices)
Dim workspace = New AdhocWorkspace()
Dim document = workspace _
.AddProject("TestProject", LanguageNames.VisualBasic) _
......
......@@ -21,8 +21,13 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context
return true;
}
return !context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword)
&& InMemberDeclarationContext(position, context, cancellationToken);
if (context.TargetToken.IsKindOrHasMatchingText(SyntaxKind.PartialKeyword))
{
return false;
}
return InMemberDeclarationContext(position, context, cancellationToken)
|| context.SyntaxTree.IsLocalFunctionDeclarationContext(position, cancellationToken);
}
private static bool InMemberDeclarationContext(int position, CSharpSyntaxContext context, CancellationToken cancellationToken)
......
......@@ -45,7 +45,8 @@ protected override bool IsValidContext(int position, CSharpSyntaxContext context
IsUnsafeParameterTypeContext(context) ||
IsUnsafeCastTypeContext(context) ||
IsUnsafeDefaultExpressionContext(context, cancellationToken) ||
context.IsFixedVariableDeclarationContext;
context.IsFixedVariableDeclarationContext ||
context.SyntaxTree.IsLocalFunctionDeclarationContext(position, cancellationToken);
}
private bool IsUnsafeDefaultExpressionContext(CSharpSyntaxContext context, CancellationToken cancellationToken)
......
......@@ -97,12 +97,11 @@ private bool IsPredefinedTypeInDeclaration(SyntaxNode declarationStatement)
private bool IsInferredPredefinedType(SyntaxNode declarationStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
TypeSyntax typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
var typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);
return typeSyntax != null
? typeSyntax.IsTypeInferred(semanticModel) &&
semanticModel.GetTypeInfo(typeSyntax).Type?.IsSpecialType() == true
: false;
return typeSyntax != null &&
typeSyntax.IsTypeInferred(semanticModel) &&
semanticModel.GetTypeInfo(typeSyntax).Type?.IsSpecialType() == true;
}
private TypeSyntax GetTypeSyntaxFromDeclaration(SyntaxNode declarationStatement)
......
......@@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.ExtractMethod;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
......@@ -381,13 +382,12 @@ private OperationStatus CheckActiveStatements(IEnumerable<StatementSyntax> state
// return survived var decls
if (list.Count > 0)
{
yield return
SyntaxFactory.LocalDeclarationStatement(
declarationStatement.Modifiers,
SyntaxFactory.VariableDeclaration(
declarationStatement.Declaration.Type,
SyntaxFactory.SeparatedList(list)),
declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
yield return SyntaxFactory.LocalDeclarationStatement(
declarationStatement.Modifiers,
SyntaxFactory.VariableDeclaration(
declarationStatement.Declaration.Type,
SyntaxFactory.SeparatedList(list)),
declarationStatement.SemicolonToken.WithPrependedLeadingTrivia(triviaList));
triviaList.Clear();
}
......@@ -603,11 +603,19 @@ protected override StatementSyntax CreateAssignmentExpressionStatement(SyntaxTok
protected override StatementSyntax CreateDeclarationStatement(
VariableInfo variable,
CancellationToken cancellationToken,
ExpressionSyntax initialValue = null)
ExpressionSyntax initialValue,
CancellationToken cancellationToken)
{
// Convert to 'var' if appropriate. Note: because we're extracting out
// to a method, the initialValue will be a method-call. Types are not
// apperant with method calls (i.e. as opposed to a 'new XXX()' expression,
// where the type is apperant).
var type = variable.GetVariableType(this.SemanticDocument);
var typeNode = type.GenerateTypeSyntax();
var typeNode = initialValue == null
? type.GenerateTypeSyntax()
: type.GenerateTypeSyntaxOrVar(
this.SemanticDocument.Document.Project.Solution.Options,
typeIsApperant: false);
var equalsValueClause = initialValue == null ? null : SyntaxFactory.EqualsValueClause(value: initialValue);
......
......@@ -16,6 +16,7 @@
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Text;
......@@ -47,22 +48,21 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
// Note: if using 'var' would cause a problem, we will use the actual type
// of hte local. This is necessary in some cases (for example, when the
// type of the out-var-decl affects overload resolution or generic instantiation).
var useVarWhenDeclaringLocals = options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals);
var useImplicitTypeForIntrinsicTypes = options.GetOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes).Value;
foreach (var diagnostic in diagnostics)
{
cancellationToken.ThrowIfCancellationRequested();
await AddEditsAsync(document, editor, diagnostic,
useVarWhenDeclaringLocals, useImplicitTypeForIntrinsicTypes,
cancellationToken).ConfigureAwait(false);
await AddEditsAsync(
document, editor, diagnostic,
options, cancellationToken).ConfigureAwait(false);
}
}
private async Task AddEditsAsync(
Document document, SyntaxEditor editor, Diagnostic diagnostic,
bool useVarWhenDeclaringLocals, bool useImplicitTypeForIntrinsicTypes, CancellationToken cancellationToken)
OptionSet options, CancellationToken cancellationToken)
{
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
// Recover the nodes we care about.
......@@ -121,8 +121,15 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
}
// get the type that we want to put in the out-var-decl based on the user's options.
// i.e. prefer 'out var' if that is what the user wants.
var newType = this.GetDeclarationType(declaration.Type, useVarWhenDeclaringLocals, useImplicitTypeForIntrinsicTypes);
// i.e. prefer 'out var' if that is what the user wants. Note: if we have:
//
// Method(out var x)
//
// Then the type is not-apperant, and we shoudl not use var if the user only wants
// it for apperant types
var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator);
var newType = local.Type.GenerateTypeSyntaxOrVar(options, typeIsApperant: false);
var declarationExpression = GetDeclarationExpression(
sourceText, identifier, newType, singleDeclarator ? null : declarator);
......@@ -131,11 +138,9 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
var semanticsChanged = await SemanticsChangedAsync(
document, declaration, invocationOrCreation, newType,
identifier, declarationExpression, cancellationToken).ConfigureAwait(false);
if (semanticsChanged)
if (semanticsChanged && newType.IsVar)
{
// Switching to 'var' changed semantics. Just use the original type of the local.
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var local = (ILocalSymbol)semanticModel.GetDeclaredSymbol(declarator);
// If the user originally wrote it something other than 'var', then use what they
// wrote. Otherwise, synthesize the actual type of the local.
......
......@@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseCollectionInitializer
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseCollectionInitializer), Shared]
internal class CSharpUseCollectionInitializerCodeFixProvider :
AbstractUseCollectionInitializerCodeFixProvider<
SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
......
......@@ -5,8 +5,6 @@
using System.Composition;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.UseObjectInitializer;
namespace Microsoft.CodeAnalysis.CSharp.UseObjectInitializer
......@@ -14,6 +12,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseObjectInitializer
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.UseObjectInitializer), Shared]
internal class CSharpUseObjectInitializerCodeFixProvider :
AbstractUseObjectInitializerCodeFixProvider<
SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
......@@ -23,7 +22,7 @@ internal class CSharpUseObjectInitializerCodeFixProvider :
{
protected override ObjectCreationExpressionSyntax GetNewObjectCreation(
ObjectCreationExpressionSyntax objectCreation,
ImmutableArray<Match<ExpressionStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax>> matches)
ImmutableArray<Match<ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, ExpressionStatementSyntax>> matches)
{
var openBrace = SyntaxFactory.Token(SyntaxKind.OpenBraceToken)
.WithTrailingTrivia(SyntaxFactory.ElasticCarriageReturnLineFeed);
......@@ -35,7 +34,7 @@ internal class CSharpUseObjectInitializerCodeFixProvider :
}
private SeparatedSyntaxList<ExpressionSyntax> CreateExpressions(
ImmutableArray<Match<ExpressionStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax>> matches)
ImmutableArray<Match<ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, ExpressionStatementSyntax>> matches)
{
var nodesAndTokens = new List<SyntaxNodeOrToken>();
for (int i = 0; i < matches.Length; i++)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
......
......@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
......@@ -212,7 +213,7 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false);
var defaultItemSpan = this.GetDefaultCompletionListSpan(text, caretPosition);
options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);;
options = options ?? await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false);
var providers = GetFilteredProviders(roles, trigger, options);
var completionProviderToIndex = GetCompletionProviderToIndex(providers);
......@@ -220,38 +221,43 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
var triggeredProviders = ImmutableArray<CompletionProvider>.Empty;
switch (trigger.Kind)
{
case CompletionTriggerKind.Insertion:
case CompletionTriggerKind.Deletion:
if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options))
{
triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
if (triggeredProviders.Length == 0)
case CompletionTriggerKind.Insertion:
case CompletionTriggerKind.Deletion:
if (this.ShouldTriggerCompletion(text, caretPosition, trigger, roles, options))
{
triggeredProviders = providers;
triggeredProviders = providers.Where(p => p.ShouldTriggerCompletion(text, caretPosition, trigger, options)).ToImmutableArrayOrEmpty();
if (triggeredProviders.Length == 0)
{
triggeredProviders = providers;
}
}
}
break;
default:
triggeredProviders = providers;
break;
break;
default:
triggeredProviders = providers;
break;
}
// Now, ask all the triggered providers if they can provide a group.
var completionContexts = new List<CompletionContext>();
foreach (var provider in triggeredProviders)
// Now, ask all the triggered providers, in parallel, to populate a completion context.
// Note: we keep any context with items *or* with a suggested item.
var triggeredCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
document, caretPosition, trigger, options,
defaultItemSpan, triggeredProviders,
cancellationToken).ConfigureAwait(false);
// If we didn't even get any back with items, then there's nothing to do.
// i.e. if only got items back that had only suggestion items, then we don't
// want to show any completion.
if (!triggeredCompletionContexts.Any(cc => cc.Items.Count > 0))
{
var completionContext = await GetContextAsync(
provider, document, caretPosition, trigger,
options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionContext != null)
{
completionContexts.Add(completionContext);
}
return null;
}
// See if there was a group provided that was exclusive and had items in it. If so, then
// All the contexts should be non-empty or have a suggestion item.
Debug.Assert(triggeredCompletionContexts.All(HasAnyItems));
// See if there was a completion context provided that was exclusive. If so, then
// that's all we'll return.
var firstExclusiveContext = completionContexts.FirstOrDefault(t => t.IsExclusive && t.Items.Any());
var firstExclusiveContext = triggeredCompletionContexts.FirstOrDefault(t => t.IsExclusive);
if (firstExclusiveContext != null)
{
......@@ -261,41 +267,51 @@ internal protected CompletionProvider GetProvider(CompletionItem item)
isExclusive: true);
}
// If no exclusive providers provided anything, then go through the remaining
// triggered list and see if any provide items.
var nonExclusiveLists = completionContexts.Where(t => !t.IsExclusive).ToList();
// Shouldn't be any exclusive completion contexts at this point.
Debug.Assert(triggeredCompletionContexts.All(cc => !cc.IsExclusive));
// If we still don't have any items, then we're definitely done.
if (!nonExclusiveLists.Any(g => g.Items.Any()))
{
return null;
}
// Great! We had some items. Now we want to see if any of the other providers
// would like to augment the completion list. For example, we might trigger
// enum-completion on space. If enum completion results in any items, then
// we'll want to augment the list with all the regular symbol completion items.
var augmentingProviders = providers.Except(triggeredProviders).ToImmutableArray();
// If we do have items, then ask all the other (non exclusive providers) if they
// want to augment the items.
var usedProviders = nonExclusiveLists.Select(g => g.Provider);
var nonUsedProviders = providers.Except(usedProviders);
var nonUsedNonExclusiveLists = new List<CompletionContext>();
foreach (var provider in nonUsedProviders)
{
var completionList = await GetContextAsync(provider, document, caretPosition, trigger, options, defaultItemSpan, cancellationToken).ConfigureAwait(false);
if (completionList != null && !completionList.IsExclusive)
{
nonUsedNonExclusiveLists.Add(completionList);
}
}
var augmentingCompletionContexts = await ComputeNonEmptyCompletionContextsAsync(
document, caretPosition, trigger, options, defaultItemSpan,
augmentingProviders, cancellationToken).ConfigureAwait(false);
var allProvidersAndLists = nonExclusiveLists.Concat(nonUsedNonExclusiveLists).ToList();
if (allProvidersAndLists.Count == 0)
{
return null;
}
var allContexts = triggeredCompletionContexts.Concat(augmentingCompletionContexts);
Debug.Assert(allContexts.Length > 0);
// Providers are ordered, but we processed them in our own order. Ensure that the
// groups are properly ordered based on the original providers.
allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
allContexts = allContexts.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]);
return MergeAndPruneCompletionLists(allContexts, defaultItemSpan, isExclusive: false);
}
private static bool HasAnyItems(CompletionContext cc)
{
return cc.Items.Count > 0 || cc.SuggestionModeItem != null;
}
private async Task<ImmutableArray<CompletionContext>> ComputeNonEmptyCompletionContextsAsync(
Document document, int caretPosition, CompletionTrigger trigger,
OptionSet options, TextSpan defaultItemSpan,
ImmutableArray<CompletionProvider> providers,
CancellationToken cancellationToken)
{
var completionContextTasks = new List<Task<CompletionContext>>();
foreach (var provider in providers)
{
completionContextTasks.Add(GetContextAsync(
provider, document, caretPosition, trigger,
options, defaultItemSpan, cancellationToken));
}
return MergeAndPruneCompletionLists(allProvidersAndLists, defaultItemSpan, isExclusive: false);
var completionContexts = await Task.WhenAll(completionContextTasks).ConfigureAwait(false);
var nonEmptyContexts = completionContexts.Where(HasAnyItems).ToImmutableArray();
return nonEmptyContexts;
}
private CompletionList MergeAndPruneCompletionLists(
......@@ -520,4 +536,4 @@ int IEqualityComparer<ImmutableHashSet<string>>.GetHashCode(ImmutableHashSet<str
return hash;
}
}
}
}
\ No newline at end of file
......@@ -102,7 +102,7 @@ public async Task<DiagnosticAnalysisResult> GetAnalysisDataAsync(Project project
}
}
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
// this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
......@@ -182,7 +182,7 @@ public async Task<DiagnosticAnalysisResult> GetProjectAnalysisDataAsync(Project
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version);
var builder = new Builder(project.Id, lastResult.Version);
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
// this can happen if SaveAsync is not yet called but active file merge happened. one of case is if user did build before the very first
// analysis happened.
......@@ -301,7 +301,7 @@ private async Task<DiagnosticAnalysisResult> LoadInitialAnalysisDataAsync(Projec
}
}
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
return new DiagnosticAnalysisResult(project.Id, VersionStamp.Default, ImmutableHashSet<DocumentId>.Empty, isEmpty: true, fromBuild: false);
}
......@@ -333,7 +333,7 @@ private async Task<DiagnosticAnalysisResult> LoadInitialProjectAnalysisDataAsync
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version);
var builder = new Builder(project.Id, version);
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, builder.AddOthers, cancellationToken).ConfigureAwait(false))
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
return new DiagnosticAnalysisResult(project.Id, VersionStamp.Default, ImmutableHashSet<DocumentId>.Empty, isEmpty: true, fromBuild: false);
}
......@@ -355,22 +355,28 @@ private async Task SerializeAsync(DiagnosticDataSerializer serializer, object do
InMemoryStorage.Cache(_owner.Analyzer, ValueTuple.Create(key, stateKey), new CacheEntry(serializer.Version, diagnostics));
}
private static Action<Builder, DocumentId, ImmutableArray<DiagnosticData>> s_addSyntaxLocals = (b, id, locals) => b.AddSyntaxLocals(id, locals);
private static Action<Builder, DocumentId, ImmutableArray<DiagnosticData>> s_addSemanticLocals = (b, id, locals) => b.AddSemanticLocals(id, locals);
private static Action<Builder, DocumentId, ImmutableArray<DiagnosticData>> s_addNonLocals = (b, id, locals) => b.AddNonLocals(id, locals);
private static Action<Builder, ProjectId, ImmutableArray<DiagnosticData>> s_addOthers = (b, id, locals) => b.AddOthers(id, locals);
private async Task<bool> TryDeserializeDocumentAsync(DiagnosticDataSerializer serializer, Document document, Builder builder, CancellationToken cancellationToken)
{
var result = true;
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, builder.AddSyntaxLocals, cancellationToken).ConfigureAwait(false);
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SemanticStateName, builder.AddSemanticLocals, cancellationToken).ConfigureAwait(false);
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.NonLocalStateName, builder.AddNonLocals, cancellationToken).ConfigureAwait(false);
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SyntaxStateName, s_addSyntaxLocals, builder, cancellationToken).ConfigureAwait(false);
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.SemanticStateName, s_addSemanticLocals, builder, cancellationToken).ConfigureAwait(false);
result &= await TryDeserializeAsync(serializer, document, document.Id, _owner.NonLocalStateName, s_addNonLocals, builder, cancellationToken).ConfigureAwait(false);
return result;
}
private async Task<bool> TryDeserializeAsync<T>(
private async Task<bool> TryDeserializeAsync<TKey, TArg>(
DiagnosticDataSerializer serializer,
object documentOrProject, T key, string stateKey,
Action<T, ImmutableArray<DiagnosticData>> add,
CancellationToken cancellationToken) where T : class
object documentOrProject, TKey key, string stateKey,
Action<TArg, TKey, ImmutableArray<DiagnosticData>> add,
TArg arg,
CancellationToken cancellationToken) where TKey : class
{
var diagnostics = await DeserializeAsync(serializer, documentOrProject, key, stateKey, cancellationToken).ConfigureAwait(false);
if (diagnostics == null)
......@@ -378,7 +384,7 @@ private async Task<bool> TryDeserializeDocumentAsync(DiagnosticDataSerializer se
return false;
}
add(key, diagnostics.Value);
add(arg, key, diagnostics.Value);
return true;
}
......
......@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
......@@ -61,7 +62,7 @@ protected CodeGenerator(InsertionPoint insertionPoint, SelectionResult selection
protected abstract Task<TNodeUnderContainer> GetStatementOrInitializerContainingInvocationToExtractedMethodAsync(SyntaxAnnotation callsiteAnnotation, CancellationToken cancellationToken);
protected abstract TExpression CreateCallSignature();
protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, CancellationToken cancellationToken, TExpression initialValue = null);
protected abstract TStatement CreateDeclarationStatement(VariableInfo variable, TExpression initialValue, CancellationToken cancellationToken);
protected abstract TStatement CreateAssignmentExpressionStatement(SyntaxToken identifier, TExpression rvalue);
protected abstract TStatement CreateReturnStatement(string identifierName = null);
......@@ -188,8 +189,10 @@ protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(Cancellati
// there must be one decl behavior when there is "return value and initialize" variable
Contract.ThrowIfFalse(this.AnalyzerResult.GetVariablesToSplitOrMoveOutToCallSite(cancellationToken).Single(v => v.ReturnBehavior == ReturnBehavior.Initialization) != null);
return statements.Concat(
CreateDeclarationStatement(variable, cancellationToken, CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
var declarationStatement = CreateDeclarationStatement(
variable, CreateCallSignature(), cancellationToken).WithAdditionalAnnotations(this.CallSiteAnnotation);
return statements.Concat(declarationStatement);
}
Contract.ThrowIfFalse(variable.ReturnBehavior == ReturnBehavior.Assignment);
......@@ -197,19 +200,22 @@ protected VariableInfo GetOutermostVariableToMoveIntoMethodDefinition(Cancellati
CreateAssignmentExpressionStatement(CreateIdentifier(variable.Name), CreateCallSignature()).WithAdditionalAnnotations(this.CallSiteAnnotation));
}
protected IEnumerable<TStatement> CreateDeclarationStatements(IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
protected IEnumerable<TStatement> CreateDeclarationStatements(
IEnumerable<VariableInfo> variables, CancellationToken cancellationToken)
{
var list = new List<TStatement>();
foreach (var variable in variables)
{
list.Add(CreateDeclarationStatement(variable, cancellationToken));
list.Add(CreateDeclarationStatement(
variable, initialValue: null, cancellationToken: cancellationToken));
}
return list;
}
protected IEnumerable<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSite(IEnumerable<TStatement> statements, CancellationToken cancellationToken)
protected IEnumerable<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSite(
IEnumerable<TStatement> statements, CancellationToken cancellationToken)
{
var list = new List<TStatement>();
......@@ -220,7 +226,8 @@ protected IEnumerable<TStatement> AddSplitOrMoveDeclarationOutStatementsToCallSi
continue;
}
list.Add(CreateDeclarationStatement(variable, cancellationToken));
list.Add(CreateDeclarationStatement(
variable, initialValue: null, cancellationToken: cancellationToken));
}
return list;
......
......@@ -140,9 +140,11 @@
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionForNullableCodeFixProvider.cs" />
<Compile Include="UseCoalesceExpression\UseCoalesceExpressionCodeFixProvider.cs" />
<Compile Include="UseCoalesceExpression\AbstractUseCoalesceExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseCollectionInitializer\ObjectCreationExpressionAnalyzer.cs" />
<Compile Include="UseExplicitTupleName\UseExplicitTupleNameCodeFixProvider.cs" />
<Compile Include="UseNullPropagation\AbstractUseNullPropagationCodeFixProvider.cs" />
<Compile Include="UseNullPropagation\AbstractUseNullPropagationDiagnosticAnalyzer.cs" />
<Compile Include="UseObjectInitializer\ObjectCreationExpressionAnalyzer.cs" />
<Compile Include="UseThrowExpression\AbstractUseThrowExpressionDiagnosticAnalyzer.cs" />
<Compile Include="UseThrowExpression\UseThrowExpressionCodeFixProvider.cs" />
<Compile Include="Structure\BlockTypes.cs" />
......@@ -306,13 +308,13 @@
<Compile Include="DocumentationComments\CodeFixes\AbstractAddDocCommentNodesCodeFixProvider.cs" />
<Compile Include="DocumentationComments\CodeFixes\AbstractRemoveDocCommentNodeCodeFixProvider.cs" />
<Compile Include="DocumentSpan.cs" />
<Compile Include="FindReferences\DefinitionItem.cs" />
<Compile Include="FindReferences\DefinitionItem.DocumentLocationDefinitionItem.cs" />
<Compile Include="FindReferences\DefinitionItem.NonNavigatingDefinitionItem.cs" />
<Compile Include="FindReferences\DefinitionItem.SymbolDefinitionItem.cs" />
<Compile Include="FindReferences\DefinitionsAndReferences.cs" />
<Compile Include="FindReferences\IDefinitionsAndReferencesFactory.cs" />
<Compile Include="FindReferences\SourceReferenceItem.cs" />
<Compile Include="FindUsages\DefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.DocumentLocationDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.NonNavigatingDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionItem.SymbolDefinitionItem.cs" />
<Compile Include="FindUsages\DefinitionsAndReferences.cs" />
<Compile Include="FindUsages\IDefinitionsAndReferencesFactory.cs" />
<Compile Include="FindUsages\SourceReferenceItem.cs" />
<Compile Include="Diagnostics\EngineV2\InProcCodeAnalysisDiagnosticAnalyzerExecutor.cs" />
<Compile Include="Diagnostics\EngineV2\ICodeAnalysisDiagnosticAnalyzerExecutor.cs" />
<Compile Include="MakeMethodAsynchronous\AbstractMakeMethodAsynchronousCodeFixProvider.cs" />
......
......@@ -3,7 +3,7 @@
using System;
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
......
......@@ -2,7 +2,7 @@
using System.Collections.Immutable;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
......
......@@ -8,7 +8,7 @@
using Microsoft.CodeAnalysis.Navigation;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
internal partial class DefinitionItem
{
......
......@@ -4,7 +4,7 @@
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Completion;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
/// <summary>
/// Information about a symbol's definition that can be displayed in an editor
......
......@@ -4,7 +4,7 @@
using System.Collections.Immutable;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
/// <summary>
/// A collection of <see cref="DefinitionItem"/>s and <see cref="SourceReferenceItem"/>s
......
......@@ -12,7 +12,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
internal interface IDefinitionsAndReferencesFactory : IWorkspaceService
{
......
......@@ -2,7 +2,7 @@
using System;
namespace Microsoft.CodeAnalysis.FindReferences
namespace Microsoft.CodeAnalysis.FindUsages
{
/// <summary>
/// Information about a symbol's reference that can be used for diplay and
......
......@@ -20,8 +20,8 @@ private class IncrementalAnalyzer : IncrementalAnalyzerBase
{
public override Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken)
{
return SyntaxTreeInfo.PrecalculateAsync(document, cancellationToken);
return SyntaxTreeIndex.PrecalculateAsync(document, cancellationToken);
}
}
}
}
}
\ No newline at end of file
......@@ -45,7 +45,7 @@ internal abstract partial class AbstractNavigateToSearchService
}
cancellationToken.ThrowIfCancellationRequested();
var declarationInfo = await document.GetDeclarationInfoAsync(cancellationToken).ConfigureAwait(false);
var declarationInfo = await document.GetSyntaxTreeIndexAsync(cancellationToken).ConfigureAwait(false);
foreach (var declaredSymbolInfo in declarationInfo.DeclaredSymbolInfos)
{
......
......@@ -9,15 +9,20 @@ internal partial class FeatureAttribute
public const string BraceHighlighting = nameof(BraceHighlighting);
public const string CallHierarchy = nameof(CallHierarchy);
public const string Classification = nameof(Classification);
public const string CodeModel = nameof(CodeModel);
public const string CompletionSet = nameof(CompletionSet);
public const string DesignerAttribute = nameof(DesignerAttribute);
public const string ErrorSquiggles = nameof(ErrorSquiggles);
public const string DiagnosticService = nameof(DiagnosticService);
public const string ErrorList = nameof(ErrorList);
public const string FindReferences = nameof(FindReferences);
public const string TodoCommentList = nameof(TodoCommentList);
public const string ErrorSquiggles = nameof(ErrorSquiggles);
public const string EventHookup = nameof(EventHookup);
public const string FindReferences = nameof(FindReferences);
public const string GlobalOperation = nameof(GlobalOperation);
public const string GoToImplementation = nameof(GoToImplementation);
public const string GraphProvider = nameof(GraphProvider);
public const string InfoBar = nameof(InfoBar);
public const string KeywordHighlighting = nameof(KeywordHighlighting);
public const string LightBulb = nameof(LightBulb);
public const string LineSeparators = nameof(LineSeparators);
public const string NavigateTo = nameof(NavigateTo);
public const string NavigationBar = nameof(NavigationBar);
......@@ -26,15 +31,11 @@ internal partial class FeatureAttribute
public const string ReferenceHighlighting = nameof(ReferenceHighlighting);
public const string Rename = nameof(Rename);
public const string RenameTracking = nameof(RenameTracking);
public const string RuleSetEditor = nameof(RuleSetEditor);
public const string SignatureHelp = nameof(SignatureHelp);
public const string Snippets = nameof(Snippets);
public const string SolutionCrawler = nameof(SolutionCrawler);
public const string TodoCommentList = nameof(TodoCommentList);
public const string Workspace = nameof(Workspace);
public const string LightBulb = nameof(LightBulb);
public const string CodeModel = nameof(CodeModel);
public const string GlobalOperation = nameof(GlobalOperation);
public const string DiagnosticService = nameof(DiagnosticService);
public const string RuleSetEditor = nameof(RuleSetEditor);
public const string InfoBar = nameof(InfoBar);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
......@@ -17,27 +18,30 @@
namespace Microsoft.CodeAnalysis.UseCollectionInitializer
{
internal abstract class AbstractUseCollectionInitializerCodeFixProvider<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
TVariableDeclarator>
: CodeFixProvider
TVariableDeclaratorSyntax>
: SyntaxEditorBasedCodeFixProvider
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
where TVariableDeclarator : SyntaxNode
where TVariableDeclaratorSyntax : SyntaxNode
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId);
protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic)
=> !diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
......@@ -46,32 +50,65 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return SpecializedTasks.EmptyTask;
}
private async Task<Document> FixAsync(
Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var objectCreation = (TObjectCreationExpressionSyntax)root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan);
// Fix-All for this feature is somewhat complicated. As Collection-Initializers
// could be arbitrarily nested, we have to make sure that any edits we make
// to one Collection-Initializer are seen by any higher ones. In order to do this
// we actually process each object-creation-node, one at a time, rewriting
// the tree for each node. In order to do this effectively, we use the '.TrackNodes'
// feature to keep track of all the object creation nodes as we make edits to
// the tree. If we didn't do this, then we wouldn't be able to find the
// second object-creation-node after we make the edit for the first one.
var workspace = document.Project.Solution.Workspace;
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var analyzer = new Analyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TInvocationExpressionSyntax, TExpressionStatementSyntax, TVariableDeclarator>(
syntaxFacts, objectCreation);
var matches = analyzer.Analyze();
var originalRoot = editor.OriginalRoot;
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var originalObjectCreationNodes = new Stack<TObjectCreationExpressionSyntax>();
foreach (var diagnostic in diagnostics)
{
var objectCreation = (TObjectCreationExpressionSyntax)originalRoot.FindNode(
diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
originalObjectCreationNodes.Push(objectCreation);
}
var statement = objectCreation.FirstAncestorOrSelf<TStatementSyntax>();
var newStatement = statement.ReplaceNode(
objectCreation,
GetNewObjectCreation(objectCreation, matches)).WithAdditionalAnnotations(Formatter.Annotation);
// We're going to be continually editing this tree. Track all the nodes we
// care about so we can find them across each edit.
var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes);
editor.ReplaceNode(statement, newStatement);
foreach (var match in matches)
while (originalObjectCreationNodes.Count > 0)
{
editor.RemoveNode(match);
var originalObjectCreation = originalObjectCreationNodes.Pop();
var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single();
var analyzer = new ObjectCreationExpressionAnalyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TInvocationExpressionSyntax, TExpressionStatementSyntax, TVariableDeclaratorSyntax>(
syntaxFacts, objectCreation);
var matches = analyzer.Analyze();
if (matches == null || matches.Value.Length == 0)
{
continue;
}
var statement = objectCreation.FirstAncestorOrSelf<TStatementSyntax>();
var newStatement = statement.ReplaceNode(
objectCreation,
GetNewObjectCreation(objectCreation, matches.Value)).WithAdditionalAnnotations(Formatter.Annotation);
var subEditor = new SyntaxEditor(currentRoot, workspace);
subEditor.ReplaceNode(statement, newStatement);
foreach (var match in matches)
{
subEditor.RemoveNode(match);
}
currentRoot = subEditor.GetChangedRoot();
}
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
editor.ReplaceNode(originalRoot, currentRoot);
return SpecializedTasks.EmptyTask;
}
protected abstract TObjectCreationExpressionSyntax GetNewObjectCreation(
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.UseCollectionInitializer
{
internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
internal abstract partial class AbstractUseCollectionInitializerDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
......@@ -18,7 +25,7 @@ internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
TVariableDeclarator>
TVariableDeclaratorSyntax>
: AbstractCodeStyleDiagnosticAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
......@@ -27,11 +34,11 @@ internal abstract class AbstractUseCollectionInitializerDiagnosticAnalyzer<
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
where TVariableDeclarator : SyntaxNode
where TVariableDeclaratorSyntax : SyntaxNode
{
public bool OpenFileOnly(Workspace workspace) => false;
protected AbstractUseCollectionInitializerDiagnosticAnalyzer()
protected AbstractUseCollectionInitializerDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseCollectionInitializerDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Simplify_collection_initialization), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Collection_initialization_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
......@@ -82,13 +89,10 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
return;
}
var syntaxFacts = GetSyntaxFactsService();
var analyzer = new Analyzer<
TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax, TInvocationExpressionSyntax,
TExpressionStatementSyntax, TVariableDeclarator>(syntaxFacts, objectCreationExpression);
var analyzer = new ObjectCreationExpressionAnalyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TInvocationExpressionSyntax, TExpressionStatementSyntax, TVariableDeclaratorSyntax>(
GetSyntaxFactsService(), objectCreationExpression);
var matches = analyzer.Analyze();
if (matches.Length == 0)
if (matches == null || matches.Value.Length == 0)
{
return;
}
......@@ -101,7 +105,7 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
objectCreationExpression.GetLocation(),
additionalLocations: locations));
FadeOutCode(context, optionSet, matches, locations);
FadeOutCode(context, optionSet, matches.Value, locations);
}
private void FadeOutCode(
......@@ -146,256 +150,4 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context, INamedTypeSymbol ien
protected abstract ISyntaxFactsService GetSyntaxFactsService();
}
internal struct Analyzer<
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
TVariableDeclaratorSyntax>
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
where TVariableDeclaratorSyntax : SyntaxNode
{
private readonly ISyntaxFactsService _syntaxFacts;
private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
private TStatementSyntax _containingStatement;
private SyntaxNodeOrToken _valuePattern;
public Analyzer(
ISyntaxFactsService syntaxFacts,
TObjectCreationExpressionSyntax objectCreationExpression) : this()
{
_syntaxFacts = syntaxFacts;
_objectCreationExpression = objectCreationExpression;
}
internal ImmutableArray<TExpressionStatementSyntax> Analyze()
{
if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
{
// Don't bother if this already has an initializer.
return ImmutableArray<TExpressionStatementSyntax>.Empty;
}
_containingStatement = _objectCreationExpression.FirstAncestorOrSelf<TStatementSyntax>();
if (_containingStatement == null)
{
return ImmutableArray<TExpressionStatementSyntax>.Empty;
}
if (!TryInitializeVariableDeclarationCase() &&
!TryInitializeAssignmentCase())
{
return ImmutableArray<TExpressionStatementSyntax>.Empty;
}
var matches = ArrayBuilder<TExpressionStatementSyntax>.GetInstance();
AddMatches(matches);
return matches.ToImmutableAndFree(); ;
}
private void AddMatches(ArrayBuilder<TExpressionStatementSyntax> matches)
{
var containingBlock = _containingStatement.Parent;
var foundStatement = false;
var seenInvocation = false;
var seenIndexAssignment = false;
foreach (var child in containingBlock.ChildNodesAndTokens())
{
if (!foundStatement)
{
if (child == _containingStatement)
{
foundStatement = true;
}
continue;
}
if (child.IsToken)
{
return;
}
var statement = child.AsNode() as TExpressionStatementSyntax;
if (statement == null)
{
return;
}
SyntaxNode instance = null;
if (!seenIndexAssignment)
{
if (TryAnalyzeAddInvocation(statement, out instance))
{
seenInvocation = true;
}
}
if (!seenInvocation)
{
if (TryAnalyzeIndexAssignment(statement, out instance))
{
seenIndexAssignment = true;
}
}
if (instance == null)
{
return;
}
if (!ValuePatternMatches((TExpressionSyntax)instance))
{
return;
}
matches.Add(statement);
}
}
private bool TryAnalyzeIndexAssignment(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
{
instance = null;
if (!_syntaxFacts.SupportsIndexingInitializer(statement.SyntaxTree.Options))
{
return false;
}
if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
{
return false;
}
SyntaxNode left, right;
_syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out right);
if (!_syntaxFacts.IsElementAccessExpression(left))
{
return false;
}
instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left);
return true;
}
private bool TryAnalyzeAddInvocation(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
{
instance = null;
var invocationExpression = _syntaxFacts.GetExpressionOfExpressionStatement(statement) as TInvocationExpressionSyntax;
if (invocationExpression == null)
{
return false;
}
var arguments = _syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
if (arguments.Count < 1)
{
return false;
}
foreach (var argument in arguments)
{
if (!_syntaxFacts.IsSimpleArgument(argument))
{
return false;
}
}
var memberAccess = _syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression) as TMemberAccessExpressionSyntax;
if (memberAccess == null)
{
return false;
}
if (!_syntaxFacts.IsSimpleMemberAccessExpression(memberAccess))
{
return false;
}
SyntaxNode memberName;
_syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess, out instance, out memberName);
string name;
int arity;
_syntaxFacts.GetNameAndArityOfSimpleName(memberName, out name, out arity);
if (arity != 0 || !name.Equals(nameof(IList.Add)))
{
return false;
}
return true;
}
private bool ValuePatternMatches(TExpressionSyntax expression)
{
if (_valuePattern.IsToken)
{
return _syntaxFacts.IsIdentifierName(expression) &&
_syntaxFacts.AreEquivalent(
_valuePattern.AsToken(),
_syntaxFacts.GetIdentifierOfSimpleName(expression));
}
else
{
return _syntaxFacts.AreEquivalent(
_valuePattern.AsNode(), expression);
}
}
private bool TryInitializeAssignmentCase()
{
if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
{
return false;
}
SyntaxNode left, right;
_syntaxFacts.GetPartsOfAssignmentStatement(_containingStatement, out left, out right);
if (right != _objectCreationExpression)
{
return false;
}
_valuePattern = left;
return true;
}
private bool TryInitializeVariableDeclarationCase()
{
if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
{
return false;
}
var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf<TVariableDeclaratorSyntax>();
if (containingDeclarator == null)
{
return false;
}
if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
{
return false;
}
_valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
return true;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.LanguageServices;
namespace Microsoft.CodeAnalysis.UseCollectionInitializer
{
internal struct ObjectCreationExpressionAnalyzer<
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TInvocationExpressionSyntax,
TExpressionStatementSyntax,
TVariableDeclaratorSyntax>
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TInvocationExpressionSyntax : TExpressionSyntax
where TExpressionStatementSyntax : TStatementSyntax
where TVariableDeclaratorSyntax : SyntaxNode
{
private readonly ISyntaxFactsService _syntaxFacts;
private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
private TStatementSyntax _containingStatement;
private SyntaxNodeOrToken _valuePattern;
public ObjectCreationExpressionAnalyzer(
ISyntaxFactsService syntaxFacts,
TObjectCreationExpressionSyntax objectCreationExpression) : this()
{
_syntaxFacts = syntaxFacts;
_objectCreationExpression = objectCreationExpression;
}
internal ImmutableArray<TExpressionStatementSyntax>? Analyze()
{
if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
{
// Don't bother if this already has an initializer.
return null;
}
_containingStatement = _objectCreationExpression.FirstAncestorOrSelf<TStatementSyntax>();
if (_containingStatement == null)
{
return null;
}
if (!TryInitializeVariableDeclarationCase() &&
!TryInitializeAssignmentCase())
{
return null;
}
var matches = ArrayBuilder<TExpressionStatementSyntax>.GetInstance();
AddMatches(matches);
return matches.ToImmutableAndFree();
}
private void AddMatches(ArrayBuilder<TExpressionStatementSyntax> matches)
{
var containingBlock = _containingStatement.Parent;
var foundStatement = false;
var seenInvocation = false;
var seenIndexAssignment = false;
foreach (var child in containingBlock.ChildNodesAndTokens())
{
if (!foundStatement)
{
if (child == _containingStatement)
{
foundStatement = true;
}
continue;
}
if (child.IsToken)
{
return;
}
var statement = child.AsNode() as TExpressionStatementSyntax;
if (statement == null)
{
return;
}
SyntaxNode instance = null;
if (!seenIndexAssignment)
{
if (TryAnalyzeAddInvocation(statement, out instance))
{
seenInvocation = true;
}
}
if (!seenInvocation)
{
if (TryAnalyzeIndexAssignment(statement, out instance))
{
seenIndexAssignment = true;
}
}
if (instance == null)
{
return;
}
if (!ValuePatternMatches((TExpressionSyntax)instance))
{
return;
}
matches.Add(statement);
}
}
private bool TryAnalyzeIndexAssignment(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
{
instance = null;
if (!_syntaxFacts.SupportsIndexingInitializer(statement.SyntaxTree.Options))
{
return false;
}
if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
{
return false;
}
_syntaxFacts.GetPartsOfAssignmentStatement(statement,
out var left, out var right);
if (!_syntaxFacts.IsElementAccessExpression(left))
{
return false;
}
instance = _syntaxFacts.GetExpressionOfElementAccessExpression(left);
return true;
}
private bool TryAnalyzeAddInvocation(
TExpressionStatementSyntax statement,
out SyntaxNode instance)
{
instance = null;
var invocationExpression = _syntaxFacts.GetExpressionOfExpressionStatement(statement) as TInvocationExpressionSyntax;
if (invocationExpression == null)
{
return false;
}
var arguments = _syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
if (arguments.Count < 1)
{
return false;
}
foreach (var argument in arguments)
{
if (!_syntaxFacts.IsSimpleArgument(argument))
{
return false;
}
}
var memberAccess = _syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression) as TMemberAccessExpressionSyntax;
if (memberAccess == null)
{
return false;
}
if (!_syntaxFacts.IsSimpleMemberAccessExpression(memberAccess))
{
return false;
}
_syntaxFacts.GetPartsOfMemberAccessExpression(memberAccess, out instance, out var memberName);
_syntaxFacts.GetNameAndArityOfSimpleName(memberName, out var name, out var arity);
if (arity != 0 || !name.Equals(nameof(IList.Add)))
{
return false;
}
return true;
}
private bool ValuePatternMatches(TExpressionSyntax expression)
{
if (_valuePattern.IsToken)
{
return _syntaxFacts.IsIdentifierName(expression) &&
_syntaxFacts.AreEquivalent(
_valuePattern.AsToken(),
_syntaxFacts.GetIdentifierOfSimpleName(expression));
}
else
{
return _syntaxFacts.AreEquivalent(
_valuePattern.AsNode(), expression);
}
}
private bool TryInitializeAssignmentCase()
{
if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
{
return false;
}
_syntaxFacts.GetPartsOfAssignmentStatement(_containingStatement,
out var left, out var right);
if (right != _objectCreationExpression)
{
return false;
}
_valuePattern = left;
return true;
}
private bool TryInitializeVariableDeclarationCase()
{
if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
{
return false;
}
var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf<TVariableDeclaratorSyntax>();
if (containingDeclarator == null)
{
return false;
}
if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
{
return false;
}
_valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
return true;
}
}
}
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
......@@ -17,25 +18,28 @@
namespace Microsoft.CodeAnalysis.UseObjectInitializer
{
internal abstract class AbstractUseObjectInitializerCodeFixProvider<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TAssignmentStatementSyntax,
TVariableDeclarator>
: CodeFixProvider
TVariableDeclaratorSyntax>
: SyntaxEditorBasedCodeFixProvider
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : TStatementSyntax
where TVariableDeclarator : SyntaxNode
where TVariableDeclaratorSyntax : SyntaxNode
{
public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
public override ImmutableArray<string> FixableDiagnosticIds
=> ImmutableArray.Create(IDEDiagnosticIds.UseObjectInitializerDiagnosticId);
protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic)
=> !diagnostic.Descriptor.CustomTags.Contains(WellKnownDiagnosticTags.Unnecessary);
public override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
......@@ -44,37 +48,71 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
return SpecializedTasks.EmptyTask;
}
private async Task<Document> FixAsync(
Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
protected override Task FixAllAsync(
Document document, ImmutableArray<Diagnostic> diagnostics,
SyntaxEditor editor, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var objectCreation = (TObjectCreationExpressionSyntax)root.FindNode(diagnostic.AdditionalLocations[0].SourceSpan);
// Fix-All for this feature is somewhat complicated. As Object-Initializers
// could be arbitrarily nested, we have to make sure that any edits we make
// to one Object-Initializer are seen by any higher ones. In order to do this
// we actually process each object-creation-node, one at a time, rewriting
// the tree for each node. In order to do this effectively, we use the '.TrackNodes'
// feature to keep track of all the object creation nodes as we make edits to
// the tree. If we didn't do this, then we wouldn't be able to find the
// second object-creation-node after we make the edit for the first one.
var workspace = document.Project.Solution.Workspace;
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var analyzer = new Analyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax, TVariableDeclarator>(
syntaxFacts, objectCreation);
var matches = analyzer.Analyze();
var editor = new SyntaxEditor(root, document.Project.Solution.Workspace);
var originalRoot = editor.OriginalRoot;
var originalObjectCreationNodes = new Stack<TObjectCreationExpressionSyntax>();
foreach (var diagnostic in diagnostics)
{
var objectCreation = (TObjectCreationExpressionSyntax)originalRoot.FindNode(
diagnostic.AdditionalLocations[0].SourceSpan, getInnermostNodeForTie: true);
originalObjectCreationNodes.Push(objectCreation);
}
var statement = objectCreation.FirstAncestorOrSelf<TStatementSyntax>();
var newStatement = statement.ReplaceNode(
objectCreation,
GetNewObjectCreation(objectCreation, matches)).WithAdditionalAnnotations(Formatter.Annotation);
// We're going to be continually editing this tree. Track all the nodes we
// care about so we can find them across each edit.
var currentRoot = originalRoot.TrackNodes(originalObjectCreationNodes);
editor.ReplaceNode(statement, newStatement);
foreach (var match in matches)
while (originalObjectCreationNodes.Count > 0)
{
editor.RemoveNode(match.Statement);
var originalObjectCreation = originalObjectCreationNodes.Pop();
var objectCreation = currentRoot.GetCurrentNodes(originalObjectCreation).Single();
var analyzer = new ObjectCreationExpressionAnalyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax, TVariableDeclaratorSyntax>(
syntaxFacts, objectCreation);
var matches = analyzer.Analyze();
if (matches == null || matches.Value.Length == 0)
{
continue;
}
var statement = objectCreation.FirstAncestorOrSelf<TStatementSyntax>();
var newStatement = statement.ReplaceNode(
objectCreation,
GetNewObjectCreation(objectCreation, matches.Value)).WithAdditionalAnnotations(Formatter.Annotation);
var subEditor = new SyntaxEditor(currentRoot, workspace);
subEditor.ReplaceNode(statement, newStatement);
foreach (var match in matches)
{
subEditor.RemoveNode(match.Statement);
}
currentRoot = subEditor.GetChangedRoot();
}
var newRoot = editor.GetChangedRoot();
return document.WithSyntaxRoot(newRoot);
editor.ReplaceNode(editor.OriginalRoot, currentRoot);
return SpecializedTasks.EmptyTask;
}
protected abstract TObjectCreationExpressionSyntax GetNewObjectCreation(
TObjectCreationExpressionSyntax objectCreation,
ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>> matches);
ImmutableArray<Match<TExpressionSyntax, TStatementSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax>> matches);
private class MyCodeAction : CodeAction.DocumentChangeAction
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.Options;
using System;
using System.Linq;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.UseObjectInitializer
{
internal abstract class AbstractUseObjectInitializerDiagnosticAnalyzer<
internal abstract partial class AbstractUseObjectInitializerDiagnosticAnalyzer<
TSyntaxKind,
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TAssignmentStatementSyntax,
TVariableDeclarator>
TVariableDeclaratorSyntax>
: AbstractCodeStyleDiagnosticAnalyzer, IBuiltInAnalyzer
where TSyntaxKind : struct
where TExpressionSyntax : SyntaxNode
......@@ -27,13 +24,13 @@ internal abstract class AbstractUseObjectInitializerDiagnosticAnalyzer<
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : TStatementSyntax
where TVariableDeclarator : SyntaxNode
where TVariableDeclaratorSyntax : SyntaxNode
{
protected abstract bool FadeOutOperatorToken { get; }
public bool OpenFileOnly(Workspace workspace) => false;
protected AbstractUseObjectInitializerDiagnosticAnalyzer()
protected AbstractUseObjectInitializerDiagnosticAnalyzer()
: base(IDEDiagnosticIds.UseObjectInitializerDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Simplify_object_initialization), FeaturesResources.ResourceManager, typeof(FeaturesResources)),
new LocalizableResourceString(nameof(FeaturesResources.Object_initialization_can_be_simplified), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
......@@ -66,11 +63,11 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
}
var syntaxFacts = GetSyntaxFactsService();
var analyzer = new Analyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax, TVariableDeclarator>(
syntaxFacts,
objectCreationExpression);
var matches = analyzer.Analyze();
if (matches.Length == 0)
var analyzer = new ObjectCreationExpressionAnalyzer<TExpressionSyntax, TStatementSyntax, TObjectCreationExpressionSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax, TVariableDeclaratorSyntax>(
syntaxFacts, objectCreationExpression);
var result = analyzer.Analyze();
if (result == null || result.Value.Length == 0)
{
return;
}
......@@ -83,13 +80,13 @@ private void AnalyzeNode(SyntaxNodeAnalysisContext context)
objectCreationExpression.GetLocation(),
additionalLocations: locations));
FadeOutCode(context, optionSet, matches, locations);
FadeOutCode(context, optionSet, result.Value, locations);
}
private void FadeOutCode(
SyntaxNodeAnalysisContext context,
OptionSet optionSet,
ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>> matches,
ImmutableArray<Match<TExpressionSyntax, TStatementSyntax, TMemberAccessExpressionSyntax, TAssignmentStatementSyntax>> matches,
ImmutableArray<Location> locations)
{
var syntaxTree = context.Node.SyntaxTree;
......@@ -134,280 +131,4 @@ public DiagnosticAnalyzerCategory GetAnalyzerCategory()
return DiagnosticAnalyzerCategory.SemanticDocumentAnalysis;
}
}
internal struct Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>
where TExpressionSyntax : SyntaxNode
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : SyntaxNode
{
public readonly TAssignmentStatementSyntax Statement;
public readonly TMemberAccessExpressionSyntax MemberAccessExpression;
public readonly TExpressionSyntax Initializer;
public Match(
TAssignmentStatementSyntax statement,
TMemberAccessExpressionSyntax memberAccessExpression,
TExpressionSyntax initializer)
{
Statement = statement;
MemberAccessExpression = memberAccessExpression;
Initializer = initializer;
}
}
internal struct Analyzer<
TExpressionSyntax,
TStatementSyntax,
TObjectCreationExpressionSyntax,
TMemberAccessExpressionSyntax,
TAssignmentStatementSyntax,
TVariableDeclaratorSyntax>
where TExpressionSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : TExpressionSyntax
where TMemberAccessExpressionSyntax : TExpressionSyntax
where TAssignmentStatementSyntax : TStatementSyntax
where TVariableDeclaratorSyntax : SyntaxNode
{
private readonly ISyntaxFactsService _syntaxFacts;
private readonly TObjectCreationExpressionSyntax _objectCreationExpression;
private TStatementSyntax _containingStatement;
private SyntaxNodeOrToken _valuePattern;
public Analyzer(
ISyntaxFactsService syntaxFacts,
TObjectCreationExpressionSyntax objectCreationExpression) : this()
{
_syntaxFacts = syntaxFacts;
_objectCreationExpression = objectCreationExpression;
}
internal ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>> Analyze()
{
if (_syntaxFacts.GetObjectCreationInitializer(_objectCreationExpression) != null)
{
// Don't bother if this already has an initializer.
return ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>>.Empty;
}
_containingStatement = _objectCreationExpression.FirstAncestorOrSelf<TStatementSyntax>();
if (_containingStatement == null)
{
return ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>>.Empty;
}
if (!TryInitializeVariableDeclarationCase() &&
!TryInitializeAssignmentCase())
{
return ImmutableArray<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>>.Empty;
}
var containingBlock = _containingStatement.Parent;
var foundStatement = false;
var matches = ArrayBuilder<Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>>.GetInstance();
HashSet<string> seenNames = null;
foreach (var child in containingBlock.ChildNodesAndTokens())
{
if (!foundStatement)
{
if (child == _containingStatement)
{
foundStatement = true;
}
continue;
}
if (child.IsToken)
{
break;
}
var statement = child.AsNode() as TAssignmentStatementSyntax;
if (statement == null)
{
break;
}
if (!_syntaxFacts.IsSimpleAssignmentStatement(statement))
{
break;
}
_syntaxFacts.GetPartsOfAssignmentStatement(
statement, out var left, out var right);
var rightExpression = right as TExpressionSyntax;
var leftMemberAccess = left as TMemberAccessExpressionSyntax;
if (!_syntaxFacts.IsSimpleMemberAccessExpression(leftMemberAccess))
{
break;
}
var expression = (TExpressionSyntax)_syntaxFacts.GetExpressionOfMemberAccessExpression(leftMemberAccess);
if (!ValuePatternMatches(expression))
{
break;
}
// Don't offer this fix if the value we're initializing is itself referenced
// on the RHS of the assignment. For example:
//
// var v = new X();
// v.Prop = v.Prop.WithSomething();
//
// Or with
//
// v = new X();
// v.Prop = v.Prop.WithSomething();
//
// In the first case, 'v' is being initialized, and so will not be available
// in the object initializer we create.
//
// In the second case we'd change semantics because we'd access the old value
// before the new value got written.
if (ExpressionContainsValuePattern(rightExpression))
{
break;
}
// If we have code like "x.v = .Length.ToString()"
// then we don't want to change this into:
//
// var x = new Whatever() With { .v = .Length.ToString() }
//
// The problem here is that .Length will change it's meaning to now refer to the
// object that we're creating in our object-creation expression.
if (ImplicitMemberAccessWouldBeAffected(rightExpression))
{
break;
}
// found a match!
seenNames = seenNames ?? new HashSet<string>();
// If we see an assignment to the same property/field, we can't convert it
// to an initializer.
var name = _syntaxFacts.GetNameOfMemberAccessExpression(leftMemberAccess);
var identifier = _syntaxFacts.GetIdentifierOfSimpleName(name);
if (!seenNames.Add(identifier.ValueText))
{
break;
}
matches.Add(new Match<TAssignmentStatementSyntax, TMemberAccessExpressionSyntax, TExpressionSyntax>(
statement, leftMemberAccess, rightExpression));
}
return matches.ToImmutableAndFree();
}
private bool ImplicitMemberAccessWouldBeAffected(SyntaxNode node)
{
if (node != null)
{
foreach (var child in node.ChildNodesAndTokens())
{
if (child.IsNode)
{
if (ImplicitMemberAccessWouldBeAffected(child.AsNode()))
{
return true;
}
}
}
if (_syntaxFacts.IsSimpleMemberAccessExpression(node))
{
var expression = _syntaxFacts.GetExpressionOfMemberAccessExpression(
node, allowImplicitTarget: true);
// If we're implicitly referencing some target that is before the
// object creation expression, then our semantics will change.
if (expression != null && expression.SpanStart < _objectCreationExpression.SpanStart)
{
return true;
}
}
}
return false;
}
private bool ExpressionContainsValuePattern(TExpressionSyntax expression)
{
foreach (var subExpression in expression.DescendantNodesAndSelf().OfType<TExpressionSyntax>())
{
if (!_syntaxFacts.IsNameOfMemberAccessExpression(subExpression))
{
if (ValuePatternMatches(subExpression))
{
return true;
}
}
}
return false;
}
private bool ValuePatternMatches(TExpressionSyntax expression)
{
if (_valuePattern.IsToken)
{
return _syntaxFacts.IsIdentifierName(expression) &&
_syntaxFacts.AreEquivalent(
_valuePattern.AsToken(),
_syntaxFacts.GetIdentifierOfSimpleName(expression));
}
else
{
return _syntaxFacts.AreEquivalent(
_valuePattern.AsNode(), expression);
}
}
private bool TryInitializeAssignmentCase()
{
if (!_syntaxFacts.IsSimpleAssignmentStatement(_containingStatement))
{
return false;
}
_syntaxFacts.GetPartsOfAssignmentStatement(
_containingStatement, out var left, out var right);
if (right != _objectCreationExpression)
{
return false;
}
_valuePattern = left;
return true;
}
private bool TryInitializeVariableDeclarationCase()
{
if (!_syntaxFacts.IsLocalDeclarationStatement(_containingStatement))
{
return false;
}
var containingDeclarator = _objectCreationExpression.FirstAncestorOrSelf<TVariableDeclaratorSyntax>();
if (containingDeclarator == null)
{
return false;
}
if (!_syntaxFacts.IsDeclaratorOfLocalDeclarationStatement(containingDeclarator, _containingStatement))
{
return false;
}
_valuePattern = _syntaxFacts.GetIdentifierOfVariableDeclarator(containingDeclarator);
return true;
}
}
}
\ No newline at end of file
......@@ -9,6 +9,7 @@ Imports Microsoft.CodeAnalysis.Formatting
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.Simplification
Imports Microsoft.CodeAnalysis.Options
Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Partial Friend Class VisualBasicMethodExtractor
......@@ -349,9 +350,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ExtractMethod
Return identifier.CreateAssignmentExpressionStatementWithValue(rvalue)
End Function
Protected Overrides Function CreateDeclarationStatement(variable As VariableInfo,
cancellationToken As CancellationToken,
Optional givenInitializer As ExpressionSyntax = Nothing) As StatementSyntax
Protected Overrides Function CreateDeclarationStatement(
variable As VariableInfo,
givenInitializer As ExpressionSyntax,
cancellationToken As CancellationToken) As StatementSyntax
Dim shouldInitializeWithNothing = (variable.GetDeclarationBehavior(cancellationToken) = DeclarationBehavior.MoveOut OrElse variable.GetDeclarationBehavior(cancellationToken) = DeclarationBehavior.SplitOut) AndAlso
(variable.ParameterModifier = ParameterBehavior.Out)
......
......@@ -10,6 +10,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCollectionInitializer
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.UseCollectionInitializer), [Shared]>
Friend Class VisualBasicUseCollectionInitializerCodeFixProvider
Inherits AbstractUseCollectionInitializerCodeFixProvider(Of
SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
......
......@@ -10,6 +10,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.UseObjectInitializer), [Shared]>
Friend Class VisualBasicUseObjectInitializerCodeFixProvider
Inherits AbstractUseObjectInitializerCodeFixProvider(Of
SyntaxKind,
ExpressionSyntax,
StatementSyntax,
ObjectCreationExpressionSyntax,
......@@ -19,7 +20,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
Protected Overrides Function GetNewObjectCreation(
objectCreation As ObjectCreationExpressionSyntax,
matches As ImmutableArray(Of Match(Of AssignmentStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax))) As ObjectCreationExpressionSyntax
matches As ImmutableArray(Of Match(Of ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, AssignmentStatementSyntax))) As ObjectCreationExpressionSyntax
Dim initializer = SyntaxFactory.ObjectMemberInitializer(
CreateFieldInitializers(matches))
......@@ -30,7 +31,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseObjectInitializer
End Function
Private Function CreateFieldInitializers(
matches As ImmutableArray(Of Match(Of AssignmentStatementSyntax, MemberAccessExpressionSyntax, ExpressionSyntax))) As SeparatedSyntaxList(Of FieldInitializerSyntax)
matches As ImmutableArray(Of Match(Of ExpressionSyntax, StatementSyntax, MemberAccessExpressionSyntax, AssignmentStatementSyntax))) As SeparatedSyntaxList(Of FieldInitializerSyntax)
Dim nodesAndTokens = New List(Of SyntaxNodeOrToken)
For i = 0 To matches.Length - 1
......
......@@ -43,6 +43,7 @@
<dependency id="System.Threading.Tasks" version="$SystemThreadingTasksVersion$"/>
<dependency id="System.Threading.Tasks.Parallel" version="$SystemThreadingTasksParallelVersion$"/>
<dependency id="System.Threading.Thread" version="$SystemThreadingThreadVersion$" exclude="Compile" />
<dependency id="System.ValueTuple" version="$SystemValueTupleVersion$" />
<dependency id="System.Xml.ReaderWriter" version="$SystemXmlReaderWriterVersion$"/>
<dependency id="System.Xml.XDocument" version="$SystemXmlXDocumentVersion$"/>
<dependency id="System.Xml.XmlDocument" version="$SystemXmlXmlDocumentVersion$" exclude="Compile" />
......
......@@ -70,9 +70,9 @@ Supported Platforms:
<file src="Exes\Toolset\System.Security.Principal.Windows.dll" target="tools" />
<file src="Exes\Toolset\System.Text.Encoding.CodePages.dll" target="tools" />
<file src="Exes\Toolset\System.Threading.Thread.dll" target="tools" />
<file src="Exes\Toolset\System.ValueTuple.dll" target="tools" />
<file src="Exes\Toolset\System.Xml.XmlDocument.dll" target="tools" />
<file src="Exes\Toolset\System.Xml.XPath.dll" target="tools" />
<file src="Exes\Toolset\System.Xml.XPath.XDocument.dll" target="tools" />
<file src="Exes\Toolset\System.ValueTuple.dll" target="tools" />
</files>
</package>
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Octokit;
namespace GitMergeBot
{
internal sealed class GitHubRepository : RepositoryBase
{
private GitHubClient _client;
public GitHubRepository(string path, string repoName, string userName, string authToken)
: base(path, repoName, userName, authToken)
{
_client = new GitHubClient(new ProductHeaderValue(userName))
{
Credentials = new Credentials(authToken)
};
}
public override async Task<bool> ShouldMakePullRequestAsync(string title)
{
return (await GetExistingMergePrsAsync(title)).Count == 0;
}
public override async Task CreatePullRequestAsync(string title, string destinationOwner, string pullRequestBranch, string prBranchSourceRemote, string sourceBranch, string destinationBranch)
{
var remoteName = $"{UserName}-{RepositoryName}";
var prMessage = $@"
This is an automatically generated pull request from {sourceBranch} into {destinationBranch}.
@dotnet/roslyn-infrastructure:
``` bash
git remote add {remoteName} ""https://github.com/{UserName}/{RepositoryName}.git""
git fetch {remoteName}
git fetch {prBranchSourceRemote}
git checkout {pullRequestBranch}
git reset --hard {prBranchSourceRemote}/{destinationBranch}
git merge {prBranchSourceRemote}/{sourceBranch}
# Fix merge conflicts
git commit
git push {remoteName} {pullRequestBranch} --force
```
Once all conflicts are resolved and all the tests pass, you are free to merge the pull request.
".Trim();
try
{
var pullRequest = await _client.PullRequest.Create(
owner: destinationOwner,
name: RepositoryName,
newPullRequest: new NewPullRequest(
title: title,
head: $"{UserName}:{pullRequestBranch}",
baseRef: destinationBranch)
{
Body = prMessage
}
);
// The reason for this delay is twofold:
//
// * Github has a bug in which it can "create" a pull request without that pull request
// being able to be commented on for a short period of time.
// * The Jenkins "comment watcher" has a bug whereby any comment posted shortly after
// pull-request creation is ignored.
//
// Thus, this delay sidesteps both of those bugs by asking for a VSI test 30 seconds after
// the creation of the PR. Ugly, yes; but the only *real* way to sidestep this would be to
// 1) Fix github, 2) Fix jenkins, and while those might be lofty goals, they are not in the
// scope of this PR.
await Task.Delay(TimeSpan.FromSeconds(30.0));
await _client.Issue.Comment.Create(destinationOwner, RepositoryName, pullRequest.Number, "@dotnet-bot test vsi please");
}
catch (Exception ex) when (DidPullRequestFailDueToNoChanges(ex))
{
Console.WriteLine("There were no commits between the specified branchs. Pull request not created.");
}
}
/// <returns>The existing open merge PRs.</returns>
private async Task<IList<PullRequest>> GetExistingMergePrsAsync(string newBranchPrefix)
{
var allPullRequests = await _client.PullRequest.GetAllForRepository(UserName, RepositoryName);
var openPrs = allPullRequests.Where(pr => pr.Head.Ref.StartsWith(newBranchPrefix) && pr.User.Login == UserName).ToList();
Console.WriteLine($"Found {openPrs.Count} existing open merge pull requests.");
foreach (var pr in openPrs)
{
Console.WriteLine($" Open PR: {pr.HtmlUrl}");
}
return openPrs;
}
/// <summary>
/// The Octokit API fails on pull request creation if the PR would have been empty, but there is no way
/// to know that ahead of time.
///
/// The fall-back is to check for a very specific "failure".
/// </summary>
private static bool DidPullRequestFailDueToNoChanges(Exception ex)
{
if (!(ex is ApiValidationException apiException))
{
return false;
}
if (apiException.ApiError.Errors.Count != 1)
{
return false;
}
var error = apiException.ApiError.Errors.Single();
return error.Message.StartsWith("No commits between");
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\..\packages\LibGit2Sharp.NativeBinaries.1.0.129\build\LibGit2Sharp.NativeBinaries.props" Condition="Exists('..\..\..\..\..\packages\LibGit2Sharp.NativeBinaries.1.0.129\build\LibGit2Sharp.NativeBinaries.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
......@@ -11,6 +12,8 @@
<AssemblyName>GitMergeBot</AssemblyName>
<TargetFrameworkVersion>v4.6</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
......@@ -32,12 +35,24 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="LibGit2Sharp, Version=0.22.0.0, Culture=neutral, PublicKeyToken=7cbde695407f0333, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\LibGit2Sharp.0.22.0\lib\net40\LibGit2Sharp.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Octokit, Version=0.17.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\Octokit.0.17.0\lib\net45\Octokit.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.ValueTuple, Version=4.0.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
<HintPath>..\..\..\..\..\packages\System.ValueTuple.4.3.0-preview1-24530-04\lib\netstandard1.0\System.ValueTuple.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
......@@ -46,16 +61,26 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="GitHubRepository.cs" />
<Compile Include="RepositoryBase.cs" />
<Compile Include="Mono.Options\Options.cs" />
<Compile Include="Options.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RepositoryType.cs" />
<Compile Include="VisualStudioOnlineRepository.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\..\packages\LibGit2Sharp.NativeBinaries.1.0.129\build\LibGit2Sharp.NativeBinaries.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\..\packages\LibGit2Sharp.NativeBinaries.1.0.129\build\LibGit2Sharp.NativeBinaries.props'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
......@@ -63,4 +88,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册