From 6b4c7251e7b39b8a53c73105aea112067ebafcba Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Tue, 2 Aug 2016 12:38:40 +0200 Subject: [PATCH 01/31] Make EncodedStringText fallback encoding lazy instead of eagerly initialized in static ctor --- .../Core/CodeAnalysisTest/Text/StringTextDecodingTests.cs | 2 +- src/Compilers/Core/Portable/EncodedStringText.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/CodeAnalysisTest/Text/StringTextDecodingTests.cs b/src/Compilers/Core/CodeAnalysisTest/Text/StringTextDecodingTests.cs index c4a56bd49b927..91b90dbb69cfe 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Text/StringTextDecodingTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Text/StringTextDecodingTests.cs @@ -44,7 +44,7 @@ private static SourceText CreateMemoryStreamBasedEncodedText(byte[] bytes, using (var stream = new MemoryStream(buffer, 0, bytes.Length, writable: true, publiclyVisible: true)) { - return EncodedStringText.Create(stream, getEncoding, readEncodingOpt, algorithm); + return EncodedStringText.Create(stream, new Lazy(getEncoding), readEncodingOpt, algorithm); } } diff --git a/src/Compilers/Core/Portable/EncodedStringText.cs b/src/Compilers/Core/Portable/EncodedStringText.cs index 70385c0173e2e..36dbb83059cb9 100644 --- a/src/Compilers/Core/Portable/EncodedStringText.cs +++ b/src/Compilers/Core/Portable/EncodedStringText.cs @@ -24,7 +24,7 @@ internal static class EncodedStringText /// 2. CodePage 1252. /// 3. Latin1. /// - private static readonly Encoding s_fallbackEncoding = GetFallbackEncoding(); + private static readonly Lazy s_fallbackEncoding = new Lazy(GetFallbackEncoding); private static Encoding GetFallbackEncoding() { @@ -72,13 +72,13 @@ internal static SourceText Create(Stream stream, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { return Create(stream, - () => s_fallbackEncoding, + s_fallbackEncoding, defaultEncoding: defaultEncoding, checksumAlgorithm: checksumAlgorithm); } // internal for testing - internal static SourceText Create(Stream stream, Func getEncoding, + internal static SourceText Create(Stream stream, Lazy getEncoding, Encoding defaultEncoding = null, SourceHashAlgorithm checksumAlgorithm = SourceHashAlgorithm.Sha1) { @@ -100,7 +100,7 @@ internal static SourceText Create(Stream stream, Func getEncoding, try { - return Decode(stream, defaultEncoding ?? getEncoding(), checksumAlgorithm, throwIfBinaryDetected: detectEncoding); + return Decode(stream, defaultEncoding ?? getEncoding.Value, checksumAlgorithm, throwIfBinaryDetected: detectEncoding); } catch (DecoderFallbackException e) { From 1e44b6eb09141ecde5d64e05e947327c74799430 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Wed, 3 Aug 2016 13:56:40 -0700 Subject: [PATCH 02/31] Fix #12755 Tweak C# type inferrer to work in an incomplete object creation expression followed by array indexing. --- .../TypeInferrer/TypeInferrerTests.cs | 19 +++++++++++++++++++ ...CSharpTypeInferenceService.TypeInferrer.cs | 15 +++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs index 40f4949536aad..0e25c4fe26412 100644 --- a/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs +++ b/src/EditorFeatures/CSharpTest/TypeInferrer/TypeInferrerTests.cs @@ -1850,5 +1850,24 @@ void Foo() }"; await TestAsync(text, "global::System.Collections.Generic.IEnumerable", testPosition: false); } + + [WorkItem(12755, "https://github.com/dotnet/roslyn/issues/12755")] + [Fact, Trait(Traits.Feature, Traits.Features.TypeInferenceService)] + public async Task TestObjectCreationBeforeArrayIndexing() + { + var text = +@"using System; +class C +{ + void M() + { + int[] array; + C p = new [||] + array[4] = 4; + } +}"; + + await TestAsync(text, "global::C", testNode: false); + } } } \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index 880b818825f36..a87d9b1f8eac1 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -702,6 +702,21 @@ private IEnumerable InferTypeInArrayCreationExpression( return SpecializedCollections.EmptyEnumerable(); } + if (previousToken.Value.GetPreviousToken().Kind() == SyntaxKind.EqualsToken) + { + // We parsed an array creation but the token before `new` is `=`. + // This could be a case like: + // + // int[] array; + // Program p = new | + // array[4] = 4; + // + // This is similar to the cases described in `InferTypeInObjectCreationExpression`. + // Again, all we have to do is back up to before `new`. + + return InferTypes(previousToken.Value.SpanStart); + } + var outerTypes = InferTypes(arrayCreationExpression); return outerTypes.Where(o => o is IArrayTypeSymbol); } From 33b59912797edf76d46aac6e4eb8ec24efcc4bb9 Mon Sep 17 00:00:00 2001 From: Ravi Chande Date: Thu, 4 Aug 2016 10:56:56 -0700 Subject: [PATCH 03/31] Fix tests --- .../LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index a87d9b1f8eac1..56aa054bb5344 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -702,7 +702,7 @@ private IEnumerable InferTypeInArrayCreationExpression( return SpecializedCollections.EmptyEnumerable(); } - if (previousToken.Value.GetPreviousToken().Kind() == SyntaxKind.EqualsToken) + if (previousToken.HasValue && previousToken.Value.GetPreviousToken().Kind() == SyntaxKind.EqualsToken) { // We parsed an array creation but the token before `new` is `=`. // This could be a case like: From 4a93642fe61ebbee7df7895f31f89a98bf82d66a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 5 Aug 2016 14:40:19 -0400 Subject: [PATCH 04/31] Fix crash in symbolkeywriting. --- .../SymbolId/SymbolKeyCompilationsTests.cs | 33 +++++++++++++++++ .../CSharpCompletionCommandHandlerTests.vb | 25 ++++++++++++- .../SymbolId/SymbolKey.SymbolKeyWriter.cs | 37 ++++++++++++++++++- 3 files changed, 92 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/SymbolId/SymbolKeyCompilationsTests.cs b/src/EditorFeatures/CSharpTest/SymbolId/SymbolKeyCompilationsTests.cs index c45982ecad654..228488ef6e106 100644 --- a/src/EditorFeatures/CSharpTest/SymbolId/SymbolKeyCompilationsTests.cs +++ b/src/EditorFeatures/CSharpTest/SymbolId/SymbolKeyCompilationsTests.cs @@ -176,6 +176,39 @@ T I.this[int index] Assert.Equal(indexer2, ResolveSymbol(indexer2, compilation, SymbolKeyComparison.None)); } + [Fact] + public void RecursiveReferenceToConstructedGeneric() + { + var src1 = +@"using System.Collections.Generic; + +class C +{ + public void M(List list) + { + var v = list.Add(default(Z)); + } +}"; + + var comp1 = CreateCompilationWithMscorlib(src1); + var comp2 = CreateCompilationWithMscorlib(src1); + + var symbols1 = GetSourceSymbols(comp1, includeLocal: true).ToList(); + var symbols2 = GetSourceSymbols(comp1, includeLocal: true).ToList(); + + // First, make sure that all the symbols in this file resolve properly + // to themselves. + ResolveAndVerifySymbolList(symbols1, symbols2, comp1); + + // Now do this for the members of types we see. We want this + // so we hit things like the members of the constructed type + // List + var members1 = symbols1.OfType().SelectMany(n => n.GetMembers()).ToList(); + var members2 = symbols2.OfType().SelectMany(n => n.GetMembers()).ToList(); + + ResolveAndVerifySymbolList(members1, members2, comp1); + } + #endregion #region "Change to symbol" diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index e5302f1364b6c..efe06e5c98b2d 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -1682,5 +1682,28 @@ class C End Using End Function + + + Public Async Function TestRecursiveGenericSymbolKey() As Task + Using state = TestState.CreateCSharpTestState( + (List list, T oldItem, T newItem) + { + $$ + } +}]]>, extraExportedTypes:={GetType(CSharpEditorFormattingService)}.ToList()) + + state.SendTypeChars("list") + state.SendTypeChars(".") + Await state.AssertCompletionSession() + state.SendTypeChars("Add") + + Await state.AssertSelectedCompletionItem("Add", description:="void List.Add(T item)") + End Using + End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs index e35e5e7b2d3c6..2bde137991713 100644 --- a/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs +++ b/src/Workspaces/Core/Portable/SymbolId/SymbolKey.SymbolKeyWriter.cs @@ -160,13 +160,46 @@ private void WriteSymbolKey(ISymbol symbol, bool first) StartKey(); symbol.Accept(this); - WriteInteger(id); if (!shouldWriteOrdinal) { - _symbolToId.Add(symbol, id); + // Note: it is possible in some situations to hit the same symbol + // multiple times. For example, if you have: + // + // Foo(List list) + // + // If we start with the symbol for "list" then we'll see the following + // chain of symbols hit: + // + // List + // Z + // Foo(List) + // List + // + // The recursion is prevented because when we hit 'Foo' we mark that + // we're writing out a signature. And, in signature mode we only write + // out the ordinal for 'Z' without recursing. However, even though + // we prevent the recursion, we still hit List twice. After writing + // the innermost one out, we'll give it a reference ID. When we + // then hit the outermost one, we want to just reuse that one. + int existingId; + if (_symbolToId.TryGetValue(symbol, out existingId)) + { + // While we recursed, we already hit this symbol. Use its ID as our + // ID. + id = existingId; + } + else + { + // Haven't hit this symbol before, write out its fresh ID. + _symbolToId.Add(symbol, id); + } } + // Now write out the ID for this symbol so that any future hits of it can + // write out a reference to it instead. + WriteInteger(id); + EndKey(); } From 6018f4a6b760b46bb62a9a630dcda380ea104f95 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 Aug 2016 21:59:36 -0400 Subject: [PATCH 05/31] Small refactoring of 'make async' provider. --- .../Async/CSharpAddAsyncCodeFixProvider.cs | 18 +++++++-- .../Async/CSharpAddAwaitCodeFixProvider.cs | 19 ++++++++-- .../AbstractAddAsyncAwaitCodeFixProvider.cs | 37 ++++++++++++++----- .../Async/AbstractAddAsyncCodeFixProvider.cs | 3 +- .../CodeFixes/Async/AbstractAsyncCodeFix.cs | 15 ++++---- .../AbstractChangeToAsyncCodeFixProvider.cs | 9 +++-- .../CodeFixes/CodeFixContextExtensions.cs | 9 +++-- .../VisualBasicAddAsyncCodeFixProvider.vb | 14 +++++-- .../VisualBasicAddAwaitCodeFixProvider.vb | 14 +++++-- 9 files changed, 101 insertions(+), 37 deletions(-) diff --git a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs index 905fa1d2525c9..bbaa127fd1c8b 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs @@ -1,5 +1,7 @@ // 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.Composition; using System.Threading; @@ -10,6 +12,7 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.Async { @@ -36,12 +39,21 @@ public override ImmutableArray FixableDiagnosticIds get { return ImmutableArray.Create(CS4032, CS4033, CS4034); } } - protected override string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) + protected override async Task> GetDataAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) { - return CSharpFeaturesResources.Make_the_containing_scope_async; + var newRoot = await GetNewRootAsync( + root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); + if (newRoot == null) + { + return null; + } + + return SpecializedCollections.SingletonList(new Data( + CSharpFeaturesResources.Make_the_containing_scope_async, + newRoot)); } - protected override async Task GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) + private async Task GetNewRootAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) { var nodeToModify = GetContainingMember(oldNode); if (nodeToModify == null) diff --git a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs index 49f5431d1af8e..deae1e1501389 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs @@ -1,5 +1,7 @@ // 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.Composition; using System.Linq; @@ -36,10 +38,21 @@ internal class CSharpAddAwaitCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvi public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0029, CS4014, CS4016); - protected override string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken) => - CSharpFeaturesResources.Insert_await; + protected override async Task> GetDataAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) + { + var newRoot = await GetNewRootAsync( + root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); + if (newRoot == null) + { + return null; + } + + return SpecializedCollections.SingletonList(new Data( + CSharpFeaturesResources.Insert_await, + newRoot)); + } - protected override Task GetNewRoot( + private Task GetNewRootAsync( SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs index 7649b1c64f0b4..d200e5e19326f 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs @@ -1,6 +1,7 @@ // 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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; @@ -9,22 +10,26 @@ namespace Microsoft.CodeAnalysis.CodeFixes.Async { internal abstract partial class AbstractAddAsyncAwaitCodeFixProvider : AbstractAsyncCodeFix { - protected abstract string GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken); - protected abstract Task GetNewRoot(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken); + protected abstract Task> GetDataAsync( + SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken); - protected override async Task GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + protected override async Task> GetCodeActionsAsync( + SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); - var newRoot = await this.GetNewRoot(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); - if (newRoot != null) + var data = await this.GetDataAsync(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); + var result = new List(); + + foreach (var item in data) { - return new MyCodeAction( - this.GetDescription(diagnostic, node, semanticModel, cancellationToken), - token => Task.FromResult(document.WithSyntaxRoot(newRoot))); + var action = new MyCodeAction( + item.Description, + c => Task.FromResult(document.WithSyntaxRoot(item.NewRoot))); + result.Add(action); } - return null; + return result; } protected static bool TryGetExpressionType( @@ -51,5 +56,17 @@ public MyCodeAction(string title, Func> create { } } + + protected struct Data + { + public readonly string Description; + public readonly SyntaxNode NewRoot; + + public Data(string description, SyntaxNode newRoot) + { + Description = description; + NewRoot = newRoot; + } + } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs index 2ae03a8e0c2f8..307b7c2f50492 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs @@ -16,7 +16,8 @@ internal abstract partial class AbstractAddAsyncCodeFixProvider : AbstractAddAsy protected abstract SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode methodNode, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol); protected abstract bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination); - protected async Task ConvertMethodToAsync(Document document, SemanticModel semanticModel, SyntaxNode methodNode, CancellationToken cancellationToken) + protected async Task ConvertMethodToAsync( + Document document, SemanticModel semanticModel, SyntaxNode methodNode, CancellationToken cancellationToken) { var methodSymbol = semanticModel.GetDeclaredSymbol(methodNode, cancellationToken) as IMethodSymbol; diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractAsyncCodeFix.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractAsyncCodeFix.cs index e682ff9f47aaf..6535d1d658aaa 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractAsyncCodeFix.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractAsyncCodeFix.cs @@ -1,5 +1,6 @@ // 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.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,7 +12,8 @@ namespace Microsoft.CodeAnalysis.CodeFixes.Async { internal abstract partial class AbstractAsyncCodeFix : CodeFixProvider { - protected abstract Task GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken); + protected abstract Task> GetCodeActionsAsync( + SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken); public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -25,12 +27,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var diagnostic = context.Diagnostics.FirstOrDefault(); - var codeAction = await GetCodeFix(root, node, context.Document, diagnostic, context.CancellationToken).ConfigureAwait(false); - - if (codeAction != null) - { - context.RegisterCodeFix(codeAction, diagnostic); - } + var codeActions = await GetCodeActionsAsync( + root, node, context.Document, diagnostic, context.CancellationToken).ConfigureAwait(false); + context.RegisterFixes(codeActions, context.Diagnostics); } private bool TryGetNode(SyntaxNode root, TextSpan span, out SyntaxNode node) @@ -46,4 +45,4 @@ private bool TryGetNode(SyntaxNode root, TextSpan span, out SyntaxNode node) return node != null; } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractChangeToAsyncCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractChangeToAsyncCodeFixProvider.cs index 75efc23bc06fb..f2b66f32626d8 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractChangeToAsyncCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractChangeToAsyncCodeFixProvider.cs @@ -1,9 +1,11 @@ // 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; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.Async { @@ -12,7 +14,8 @@ internal abstract partial class AbstractChangeToAsyncCodeFixProvider : AbstractA protected abstract Task GetDescription(Diagnostic diagnostic, SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken); protected abstract Task> GetRootInOtherSyntaxTree(SyntaxNode node, SemanticModel semanticModel, Diagnostic diagnostic, CancellationToken cancellationToken); - protected override async Task GetCodeFix(SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken) + protected override async Task> GetCodeActionsAsync( + SyntaxNode root, SyntaxNode node, Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); @@ -22,9 +25,9 @@ protected override async Task GetCodeFix(SyntaxNode root, SyntaxNode var syntaxTree = result.Item1; var newRoot = result.Item2; var otherDocument = document.Project.Solution.GetDocument(syntaxTree); - return new MyCodeAction( + return SpecializedCollections.SingletonList(new MyCodeAction( await this.GetDescription(diagnostic, node, semanticModel, cancellationToken).ConfigureAwait(false), - token => Task.FromResult(otherDocument.WithSyntaxRoot(newRoot))); + token => Task.FromResult(otherDocument.WithSyntaxRoot(newRoot)))); } return null; diff --git a/src/Features/Core/Portable/CodeFixes/CodeFixContextExtensions.cs b/src/Features/Core/Portable/CodeFixes/CodeFixContextExtensions.cs index b9d045b97c0ed..8a1226e45a66a 100644 --- a/src/Features/Core/Portable/CodeFixes/CodeFixContextExtensions.cs +++ b/src/Features/Core/Portable/CodeFixes/CodeFixContextExtensions.cs @@ -24,10 +24,13 @@ internal static void RegisterFixes(this CodeFixContext context, IEnumerable internal static void RegisterFixes(this CodeFixContext context, IEnumerable actions, ImmutableArray diagnostics) { - foreach (var action in actions) + if (actions != null) { - context.RegisterCodeFix(action, diagnostics); + foreach (var action in actions) + { + context.RegisterCodeFix(action, diagnostics); + } } } } -} +} \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb index ea13f481ca3f2..7ee25914842f2 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb @@ -27,11 +27,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async End Get End Property - Protected Overrides Function GetDescription(diagnostic As Diagnostic, node As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As String - Return VBFeaturesResources.Make_the_containing_scope_Async + Protected Overrides Async Function GetDataAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of Data)) + Dim newRoot = Await GetNewRootAsync( + root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(False) + If newRoot Is Nothing Then + Return Nothing + End If + + Return SpecializedCollections.SingletonList(New Data( + VBFeaturesResources.Make_the_containing_scope_Async, + newRoot)) End Function - Protected Overrides Async Function GetNewRoot(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) + Private Async Function GetNewRootAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim methodNode = GetContainingMember(oldNode) If methodNode Is Nothing Then Return Nothing diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb index 92fa5c237e9e2..322f24d830e31 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb @@ -29,11 +29,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async End Get End Property - Protected Overrides Function GetDescription(diagnostic As Diagnostic, node As SyntaxNode, semanticModel As SemanticModel, cancellationToken As CancellationToken) As String - Return VBFeaturesResources.Insert_Await + Protected Overrides Async Function GetDataAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of Data)) + Dim newRoot = Await GetNewRootAsync( + root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(False) + If newRoot Is Nothing Then + Return Nothing + End If + + Return SpecializedCollections.SingletonList(New Data( + VBFeaturesResources.Insert_Await, + newRoot)) End Function - Protected Overrides Function GetNewRoot(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) + Private Function GetNewRootAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim expression = TryCast(oldNode, ExpressionSyntax) If expression Is Nothing Then Return SpecializedTasks.Default(Of SyntaxNode)() From a3d7f284c7a9a5eb2d9c710992ece75ce908a64a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 Aug 2016 23:35:30 -0400 Subject: [PATCH 06/31] Offer to make an 'async void' or 'async Task' method. --- .../Diagnostics/Async/AddAsyncTests.cs | 28 +++++ .../Diagnostics/Async/AddAwaitTests.cs | 11 +- .../CSharpFeaturesResources.Designer.cs | 9 -- .../Portable/CSharpFeaturesResources.resx | 3 - .../Async/CSharpAddAsyncCodeFixProvider.cs | 108 +++++++++--------- .../Async/CSharpAddAwaitCodeFixProvider.cs | 5 +- .../AbstractAddAsyncAwaitCodeFixProvider.cs | 17 ++- .../Async/AbstractAddAsyncCodeFixProvider.cs | 54 +++++++-- .../Portable/FeaturesResources.Designer.cs | 18 +++ .../Core/Portable/FeaturesResources.resx | 6 + .../VisualBasicAddAsyncCodeFixProvider.vb | 52 ++++++--- .../VisualBasicAddAwaitCodeFixProvider.vb | 4 +- .../Portable/VBFeaturesResources.Designer.vb | 9 -- .../Portable/VBFeaturesResources.resx | 3 - 14 files changed, 215 insertions(+), 112 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAsyncTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAsyncTests.cs index 14e6b4a6ea5f6..826dda4d49e6e 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAsyncTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAsyncTests.cs @@ -40,6 +40,34 @@ public static async void Test() }"; await TestAsync(initial, expected); } + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)] + public async Task AwaitInVoidMethodWithModifiers2() + { + var initial = +@"using System; +using System.Threading.Tasks; + +class Program +{ + public static void Test() + { + [|await Task.Delay(1);|] + } +}"; + + var expected = +@"using System; +using System.Threading.Tasks; + +class Program +{ + public static async Task Test() + { + await Task.Delay(1); + } +}"; + await TestAsync(initial, expected, index: 1); + } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAsync)] public async Task AwaitInTaskMethodNoModifiers() diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAwaitTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAwaitTests.cs index f91133d9ad385..12dee49297f84 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAwaitTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/Async/AddAwaitTests.cs @@ -497,7 +497,16 @@ await TestAsync( public async Task TestAssignmentExpressionWithConversionInNonAsyncFunction() { await TestMissingAsync( -@"using System . Threading . Tasks ; class TestClass { private Task MyTestMethod1Async ( ) { long myInt = [|MyIntMethodAsync ( )|] ; } private Task < int > MyIntMethodAsync ( ) { return Task . FromResult ( result : 1 ) ; } } "); +@"using System . Threading . Tasks ; +class TestClass { + private Task MyTestMethod1Async ( ) { + long myInt = [|MyIntMethodAsync ( )|] ; + } + + private Task < int > MyIntMethodAsync ( ) { + return Task . FromResult ( result : 1 ) ; + } +} "); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddAwait)] diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs index a189d87ffca87..d39049e97648f 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs @@ -548,15 +548,6 @@ internal static string Make_0_return_Task_instead_of_void { } } - /// - /// Looks up a localized string similar to Make the containing scope 'async'.. - /// - internal static string Make_the_containing_scope_async { - get { - return ResourceManager.GetString("Make_the_containing_scope_async", resourceCulture); - } - } - /// /// Looks up a localized string similar to <member name> = . /// diff --git a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx index ee60746116c07..a6ead4e6be7e9 100644 --- a/src/Features/CSharp/Portable/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/Portable/CSharpFeaturesResources.resx @@ -255,9 +255,6 @@ Insert 'await'. - - Make the containing scope 'async'. - Make {0} return Task instead of void. diff --git a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs index bbaa127fd1c8b..c0fe4b73b6eba 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAsyncCodeFixProvider.cs @@ -1,9 +1,9 @@ // 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.Composition; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; @@ -39,21 +39,7 @@ public override ImmutableArray FixableDiagnosticIds get { return ImmutableArray.Create(CS4032, CS4033, CS4034); } } - protected override async Task> GetDataAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) - { - var newRoot = await GetNewRootAsync( - root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); - if (newRoot == null) - { - return null; - } - - return SpecializedCollections.SingletonList(new Data( - CSharpFeaturesResources.Make_the_containing_scope_async, - newRoot)); - } - - private async Task GetNewRootAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) + protected override async Task> GetDataAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) { var nodeToModify = GetContainingMember(oldNode); if (nodeToModify == null) @@ -61,13 +47,17 @@ private async Task GetNewRootAsync(SyntaxNode root, SyntaxNode oldNo return null; } - var modifiedNode = await ConvertToAsync(nodeToModify, semanticModel, document, cancellationToken).ConfigureAwait(false); - if (modifiedNode != null) + var nodesAndDescriptions = await ConvertToAsync(nodeToModify, semanticModel, document, cancellationToken).ConfigureAwait(false); + if (nodesAndDescriptions == null) { - return root.ReplaceNode(nodeToModify, modifiedNode); + return null; } - return null; + var q = from n in nodesAndDescriptions + let newRoot = root.ReplaceNode(nodeToModify, n.Node) + select new DescriptionAndNode(n.Description, newRoot); + + return q.ToList(); } private static SyntaxNode GetContainingMember(SyntaxNode oldNode) @@ -98,47 +88,58 @@ private static SyntaxNode GetContainingMember(SyntaxNode oldNode) return null; } - private Task ConvertToAsync(SyntaxNode node, SemanticModel semanticModel, Document document, CancellationToken cancellationToken) + private Task> ConvertToAsync( + SyntaxNode node, SemanticModel semanticModel, Document document, CancellationToken cancellationToken) { - return node.TypeSwitch( - (MethodDeclarationSyntax methodNode) => ConvertMethodToAsync(document, semanticModel, methodNode, cancellationToken), - (ParenthesizedLambdaExpressionSyntax parenthesizedLambda) => Task.FromResult(ConvertParenthesizedLambdaToAsync(parenthesizedLambda)), - (SimpleLambdaExpressionSyntax simpleLambda) => Task.FromResult(ConvertSimpleLambdaToAsync(simpleLambda)), - (AnonymousMethodExpressionSyntax anonymousMethod) => Task.FromResult(ConvertAnonymousMethodToAsync(anonymousMethod)), - @default => Task.FromResult(null)); + if (node is MethodDeclarationSyntax) + { + return ConvertMethodToAsync(document, semanticModel, node, cancellationToken); + } + + var newNode = node.TypeSwitch( + (ParenthesizedLambdaExpressionSyntax parenthesizedLambda) => ConvertParenthesizedLambdaToAsync(parenthesizedLambda), + (SimpleLambdaExpressionSyntax simpleLambda) => ConvertSimpleLambdaToAsync(simpleLambda), + (AnonymousMethodExpressionSyntax anonymousMethod) => ConvertAnonymousMethodToAsync(anonymousMethod)); + + return Task.FromResult(SpecializedCollections.SingletonList(new DescriptionAndNode( + FeaturesResources.Make_containing_scope_async, + newNode))); } - private static SyntaxNode ConvertParenthesizedLambdaToAsync(ParenthesizedLambdaExpressionSyntax parenthesizedLambda) + private static SyntaxNode ConvertParenthesizedLambdaToAsync( + ParenthesizedLambdaExpressionSyntax parenthesizedLambda) { return SyntaxFactory.ParenthesizedLambdaExpression( - SyntaxFactory.Token(SyntaxKind.AsyncKeyword), - parenthesizedLambda.ParameterList, - parenthesizedLambda.ArrowToken, - parenthesizedLambda.Body) - .WithTriviaFrom(parenthesizedLambda) - .WithAdditionalAnnotations(Formatter.Annotation); + SyntaxFactory.Token(SyntaxKind.AsyncKeyword), + parenthesizedLambda.ParameterList, + parenthesizedLambda.ArrowToken, + parenthesizedLambda.Body) + .WithTriviaFrom(parenthesizedLambda) + .WithAdditionalAnnotations(Formatter.Annotation); } - private static SyntaxNode ConvertSimpleLambdaToAsync(SimpleLambdaExpressionSyntax simpleLambda) + private static SyntaxNode ConvertSimpleLambdaToAsync( + SimpleLambdaExpressionSyntax simpleLambda) { return SyntaxFactory.SimpleLambdaExpression( - SyntaxFactory.Token(SyntaxKind.AsyncKeyword), - simpleLambda.Parameter, - simpleLambda.ArrowToken, - simpleLambda.Body) - .WithTriviaFrom(simpleLambda) - .WithAdditionalAnnotations(Formatter.Annotation); + SyntaxFactory.Token(SyntaxKind.AsyncKeyword), + simpleLambda.Parameter, + simpleLambda.ArrowToken, + simpleLambda.Body) + .WithTriviaFrom(simpleLambda) + .WithAdditionalAnnotations(Formatter.Annotation); } - private static SyntaxNode ConvertAnonymousMethodToAsync(AnonymousMethodExpressionSyntax anonymousMethod) + private static SyntaxNode ConvertAnonymousMethodToAsync( + AnonymousMethodExpressionSyntax anonymousMethod) { return SyntaxFactory.AnonymousMethodExpression( - SyntaxFactory.Token(SyntaxKind.AsyncKeyword), - anonymousMethod.DelegateKeyword, - anonymousMethod.ParameterList, - anonymousMethod.Block) - .WithTriviaFrom(anonymousMethod) - .WithAdditionalAnnotations(Formatter.Annotation); + SyntaxFactory.Token(SyntaxKind.AsyncKeyword), + anonymousMethod.DelegateKeyword, + anonymousMethod.ParameterList, + anonymousMethod.Block) + .WithTriviaFrom(anonymousMethod) + .WithAdditionalAnnotations(Formatter.Annotation); } protected override SyntaxNode AddAsyncKeyword(SyntaxNode node) @@ -154,7 +155,8 @@ protected override SyntaxNode AddAsyncKeyword(SyntaxNode node) .WithAdditionalAnnotations(Formatter.Annotation); } - protected override SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode node, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol) + protected override SyntaxNode AddAsyncKeywordAndTaskReturnType( + SyntaxNode node, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol) { var methodNode = node as MethodDeclarationSyntax; if (methodNode == null) @@ -167,7 +169,11 @@ protected override SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode node, return null; } - var returnType = taskTypeSymbol.Construct(existingReturnType).GenerateTypeSyntax(); + var returnTypeSymbol = existingReturnType == null + ? taskTypeSymbol + : taskTypeSymbol.Construct(existingReturnType); + + var returnType = returnTypeSymbol.GenerateTypeSyntax(); return AddAsyncKeyword(methodNode.WithReturnType(returnType)); } @@ -176,4 +182,4 @@ protected override bool DoesConversionExist(Compilation compilation, ITypeSymbol return compilation.ClassifyConversion(source, destination).Exists; } } -} +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs index deae1e1501389..0415efafa693f 100644 --- a/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/Async/CSharpAddAwaitCodeFixProvider.cs @@ -38,7 +38,8 @@ internal class CSharpAddAwaitCodeFixProvider : AbstractAddAsyncAwaitCodeFixProvi public override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create(CS0029, CS4014, CS4016); - protected override async Task> GetDataAsync(SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) + protected override async Task> GetDataAsync( + SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken) { var newRoot = await GetNewRootAsync( root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); @@ -47,7 +48,7 @@ protected override async Task> GetDataAsync(SyntaxNode root, SyntaxN return null; } - return SpecializedCollections.SingletonList(new Data( + return SpecializedCollections.SingletonList(new DescriptionAndNode( CSharpFeaturesResources.Insert_await, newRoot)); } diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs index d200e5e19326f..f3a1a370d9bf7 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncAwaitCodeFixProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes.Async { internal abstract partial class AbstractAddAsyncAwaitCodeFixProvider : AbstractAsyncCodeFix { - protected abstract Task> GetDataAsync( + protected abstract Task> GetDataAsync( SyntaxNode root, SyntaxNode oldNode, SemanticModel semanticModel, Diagnostic diagnostic, Document document, CancellationToken cancellationToken); protected override async Task> GetCodeActionsAsync( @@ -19,13 +19,18 @@ protected override async Task> GetCodeActionsAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var data = await this.GetDataAsync(root, node, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(false); + if (data == null) + { + return null; + } + var result = new List(); foreach (var item in data) { var action = new MyCodeAction( item.Description, - c => Task.FromResult(document.WithSyntaxRoot(item.NewRoot))); + c => Task.FromResult(document.WithSyntaxRoot(item.Node))); result.Add(action); } @@ -57,15 +62,15 @@ public MyCodeAction(string title, Func> create } } - protected struct Data + protected struct DescriptionAndNode { public readonly string Description; - public readonly SyntaxNode NewRoot; + public readonly SyntaxNode Node; - public Data(string description, SyntaxNode newRoot) + public DescriptionAndNode(string description, SyntaxNode node) { Description = description; - NewRoot = newRoot; + Node = node; } } } diff --git a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs index 307b7c2f50492..16c911b0e7092 100644 --- a/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/Async/AbstractAddAsyncCodeFixProvider.cs @@ -1,10 +1,12 @@ // 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.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.FindSymbols; using Microsoft.CodeAnalysis.LanguageServices; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.Async { @@ -16,26 +18,62 @@ internal abstract partial class AbstractAddAsyncCodeFixProvider : AbstractAddAsy protected abstract SyntaxNode AddAsyncKeywordAndTaskReturnType(SyntaxNode methodNode, ITypeSymbol existingReturnType, INamedTypeSymbol taskTypeSymbol); protected abstract bool DoesConversionExist(Compilation compilation, ITypeSymbol source, ITypeSymbol destination); - protected async Task ConvertMethodToAsync( + protected async Task> ConvertMethodToAsync( Document document, SemanticModel semanticModel, SyntaxNode methodNode, CancellationToken cancellationToken) { var methodSymbol = semanticModel.GetDeclaredSymbol(methodNode, cancellationToken) as IMethodSymbol; + var compilation = semanticModel.Compilation; + var taskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTask); + if (methodSymbol.ReturnsVoid) { - return AddAsyncKeyword(methodNode); + return HandleVoidMethod(methodNode, taskSymbol); } - var returnType = methodSymbol.ReturnType; - var compilation = semanticModel.Compilation; - - var taskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTask); var genericTaskSymbol = compilation.GetTypeByMetadataName(SystemThreadingTasksTaskT); - if (taskSymbol == null) + if (taskSymbol == null || genericTaskSymbol == null) { return null; } + var newNode = await AddAsyncKeywordAsync( + document, methodNode, methodSymbol, compilation, + taskSymbol, genericTaskSymbol, cancellationToken).ConfigureAwait(false); + return SpecializedCollections.SingletonList( + new DescriptionAndNode(FeaturesResources.Make_containing_scope_async, newNode)); + } + + private IList HandleVoidMethod(SyntaxNode methodNode, INamedTypeSymbol taskSymbol) + { + var result = new List(); + + var method = AddAsyncKeyword(methodNode); + if (method != null) + { + result.Add(new DescriptionAndNode( + FeaturesResources.Make_containing_scope_async, + method)); + } + + method = AddAsyncKeywordAndTaskReturnType( + methodNode, existingReturnType: null, taskTypeSymbol: taskSymbol); + if (method != null) + { + result.Add(new DescriptionAndNode( + FeaturesResources.Make_containing_scope_async_return_Task, + method)); + } + + return result; + } + + private async Task AddAsyncKeywordAsync( + Document document, SyntaxNode methodNode, IMethodSymbol methodSymbol, + Compilation compilation, INamedTypeSymbol taskSymbol, + INamedTypeSymbol genericTaskSymbol, CancellationToken cancellationToken) + { + var returnType = methodSymbol.ReturnType; if (returnType is IErrorTypeSymbol) { // The return type of the method will not bind. This could happen for a lot of reasons. @@ -65,4 +103,4 @@ protected async Task ConvertMethodToAsync( return AddAsyncKeywordAndTaskReturnType(methodNode, returnType, genericTaskSymbol); } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 36ccbe548a2ce..43c723b212c5e 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -1620,6 +1620,24 @@ internal static string location_unknown { } } + /// + /// Looks up a localized string similar to Make containing scope async. + /// + internal static string Make_containing_scope_async { + get { + return ResourceManager.GetString("Make_containing_scope_async", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Make containing scope async (return Task). + /// + internal static string Make_containing_scope_async_return_Task { + get { + return ResourceManager.GetString("Make_containing_scope_async_return_Task", resourceCulture); + } + } + /// /// Looks up a localized string similar to Make method synchronous.. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index b314a4ce56bce..6675aaa7d916e 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1046,4 +1046,10 @@ This version used in: {2} Remove tag + + Make containing scope async + + + Make containing scope async (return Task) + \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb index 7ee25914842f2..8d87cc9e9d084 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAsyncCodeFixProvider.vb @@ -27,28 +27,30 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async End Get End Property - Protected Overrides Async Function GetDataAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of Data)) - Dim newRoot = Await GetNewRootAsync( - root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(False) - If newRoot Is Nothing Then - Return Nothing - End If - - Return SpecializedCollections.SingletonList(New Data( - VBFeaturesResources.Make_the_containing_scope_Async, - newRoot)) - End Function + Protected Overrides Async Function GetDataAsync( + root As SyntaxNode, oldNode As SyntaxNode, + semanticModel As SemanticModel, diagnostic As Diagnostic, + document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of DescriptionAndNode)) - Private Async Function GetNewRootAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) Dim methodNode = GetContainingMember(oldNode) If methodNode Is Nothing Then Return Nothing End If - Return root.ReplaceNode(methodNode, Await ConvertToAsync(methodNode, semanticModel, document, cancellationToken).ConfigureAwait(False)) + + Dim descriptionsAndNodes = Await ConvertToAsync( + methodNode, semanticModel, document, cancellationToken).ConfigureAwait(False) + If descriptionsAndNodes Is Nothing Then + Return Nothing + End If + + Dim q = From n In descriptionsAndNodes + Let newRoot = root.ReplaceNode(methodNode, n.Node) + Select New DescriptionAndNode(n.Description, newRoot) + + Return q.ToList() End Function Private Shared Function GetContainingMember(oldNode As SyntaxNode) As StatementSyntax - Dim lambda = oldNode.GetAncestor(Of LambdaExpressionSyntax) If lambda IsNot Nothing Then Return TryCast(lambda.ChildNodes().FirstOrDefault(Function(a) _ @@ -68,7 +70,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async Return Nothing End Function - Private Async Function ConvertToAsync(node As StatementSyntax, semanticModel As SemanticModel, document As Document, cancellationToken As CancellationToken) As Task(Of SyntaxNode) + Private Async Function ConvertToAsync(node As StatementSyntax, + semanticModel As SemanticModel, + document As Document, + cancellationToken As CancellationToken) As Task(Of IList(Of DescriptionAndNode)) Dim methodNode = TryCast(node, MethodStatementSyntax) If methodNode IsNot Nothing Then Return Await ConvertMethodToAsync(document, semanticModel, methodNode, cancellationToken).ConfigureAwait(False) @@ -76,7 +81,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async Dim lambdaNode = TryCast(node, LambdaHeaderSyntax) If lambdaNode IsNot Nothing Then - Return lambdaNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)).WithAdditionalAnnotations(Formatter.Annotation) + Dim newNode = lambdaNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)).WithAdditionalAnnotations(Formatter.Annotation) + Return SpecializedCollections.SingletonList(New DescriptionAndNode( + FeaturesResources.Make_containing_scope_async, + newNode)) End If Return Nothing @@ -99,15 +107,23 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async Return methodNode.AddModifiers(SyntaxFactory.Token(SyntaxKind.AsyncKeyword)).WithAdditionalAnnotations(Formatter.Annotation) End Function - Protected Overrides Function AddAsyncKeywordAndTaskReturnType(node As SyntaxNode, existingReturnType As ITypeSymbol, taskTypeSymbol As INamedTypeSymbol) As SyntaxNode + Protected Overrides Function AddAsyncKeywordAndTaskReturnType( + node As SyntaxNode, + existingReturnType As ITypeSymbol, + taskTypeSymbol As INamedTypeSymbol) As SyntaxNode Dim methodNode = TryCast(node, MethodStatementSyntax) If methodNode Is Nothing Then Return Nothing End If + If taskTypeSymbol Is Nothing Then Return Nothing End If + If existingReturnType Is Nothing Then + Return Nothing + End If + Dim returnType = taskTypeSymbol.Construct(existingReturnType).GenerateTypeSyntax() Return AddAsyncKeyword(methodNode.WithAsClause(methodNode.AsClause.WithType(returnType))) End Function @@ -116,4 +132,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async Return compilation.ClassifyConversion(source, destination).Exists End Function End Class -End Namespace +End Namespace \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb index 322f24d830e31..3116c56c2b7ff 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/Async/VisualBasicAddAwaitCodeFixProvider.vb @@ -29,14 +29,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Async End Get End Property - Protected Overrides Async Function GetDataAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of Data)) + Protected Overrides Async Function GetDataAsync(root As SyntaxNode, oldNode As SyntaxNode, semanticModel As SemanticModel, diagnostic As Diagnostic, document As Document, cancellationToken As CancellationToken) As Task(Of IList(Of DescriptionAndNode)) Dim newRoot = Await GetNewRootAsync( root, oldNode, semanticModel, diagnostic, document, cancellationToken).ConfigureAwait(False) If newRoot Is Nothing Then Return Nothing End If - Return SpecializedCollections.SingletonList(New Data( + Return SpecializedCollections.SingletonList(New DescriptionAndNode( VBFeaturesResources.Insert_Await, newRoot)) End Function diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb b/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb index 0ba4ee33e8750..a505ddb1bb233 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.Designer.vb @@ -1502,15 +1502,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources End Get End Property - ''' - ''' Looks up a localized string similar to Make the containing scope 'Async'.. - ''' - Friend ReadOnly Property Make_the_containing_scope_Async() As String - Get - Return ResourceManager.GetString("Make_the_containing_scope_Async", resourceCulture) - End Get - End Property - ''' ''' Looks up a localized string similar to Mid statement. ''' diff --git a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx index 392a53e826ff5..aa9536752e088 100644 --- a/src/Features/VisualBasic/Portable/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/Portable/VBFeaturesResources.resx @@ -979,9 +979,6 @@ Sub(<parameterList>) <statement> Insert 'Await'. - - Make the containing scope 'Async'. - Make {0} an Async Function. From eb5d3f83c07e2494dd5901c07fe8950e23e95b7e Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Thu, 4 Aug 2016 18:10:04 -0700 Subject: [PATCH 07/31] removing ShowErrorInfoForCodeFix --- .../EditorLayerExtensionManager.cs | 15 ++-- .../Workspaces/EditorErrorReportingService.cs | 5 -- .../VisualStudioErrorReportingService.cs | 70 ------------------- .../IErrorReportingService.cs | 1 - .../Portable/WorkspacesResources.Designer.cs | 27 +++++++ .../Core/Portable/WorkspacesResources.resx | 9 +++ 6 files changed, 46 insertions(+), 81 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs index 2af1bd32981ef..73604b9cce274 100644 --- a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs +++ b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs @@ -69,11 +69,11 @@ public override void HandleException(object provider, Exception exception) { base.HandleException(provider, exception); - _errorReportingService?.ShowErrorInfoForCodeFix( - codefixName: provider.GetType().Name, - OnEnable: () => { EnableProvider(provider); LogEnableProvider(provider); }, - OnEnableAndIgnore: () => { EnableProvider(provider); IgnoreProvider(provider); LogEnableAndIgnoreProvider(provider); }, - OnClose: () => LogLeaveDisabled(provider)); + _errorReportingService?.ShowErrorInfo(String.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, provider.GetType().Name), + new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.HyperLink, () => LaunchExceptionInfoWindow(exception)), + new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), + new ErrorReportingUI(WorkspacesResources.Enable_and_ignore_future_errors, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), + new ErrorReportingUI(String.Empty, ErrorReportingUI.UIKind.Close, () => LogLeaveDisabled(provider))); } else { @@ -93,6 +93,11 @@ public override void HandleException(object provider, Exception exception) _errorLoggerService?.LogException(provider, exception); } + private void LaunchExceptionInfoWindow(Exception exception) + { + throw new NotImplementedException(); + } + private static void LogLeaveDisabled(object provider) { LogAction(CodefixInfobar_LeaveDisabled, provider); diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs index 47ab1704e5ecc..939ffbd6838fe 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs @@ -8,11 +8,6 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { internal class EditorErrorReportingService : IErrorReportingService { - public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose) - { - ShowErrorInfo($"{codefixName} crashed"); - } - public void ShowErrorInfo(string message, params ErrorReportingUI[] items) { Logger.Log(FunctionId.Extension_Exception, message); diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs index a1e6929440d56..153342ebdc56b 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioErrorReportingService.cs @@ -31,20 +31,6 @@ public VisualStudioErrorReportingService( _listener = listener; } - public void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose) - { - // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. - _foregroundNotificationService.RegisterNotification(() => - { - IVsWindowFrame frame; - IVsInfoBarUIFactory factory; - if (_workspace.TryGetInfoBarData(out frame, out factory)) - { - CreateInfoBarForCodeFix(factory, frame, string.Format(ServicesVSResources._0_encountered_an_error_and_has_been_disabled, codefixName), OnClose, OnEnable, OnEnableAndIgnore); - } - }, _listener.BeginAsyncOperation("Show InfoBar")); - } - public void ShowErrorInfo(string message, params ErrorReportingUI[] items) { // We can be called from any thread since errors can occur anywhere, however we can only construct and InfoBar from the UI thread. @@ -161,62 +147,6 @@ public void OnClosed(IVsInfoBarUIElement infoBarUIElement) } } - private void CreateInfoBarForCodeFix(IVsInfoBarUIFactory factory, IVsWindowFrame frame, string message, Action onClose, Action onEnable = null, Action onEnableAndIgnore = null) - { - object unknown; - if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID7.VSFPROPID_InfoBarHost, out unknown))) - { - return; - } - - var textSpans = new List() - { - new InfoBarTextSpan(message) - }; - - // create action item list - var actionItems = new List(); - if (onEnable != null) - { - actionItems.Add(s_enableItem); - } - - if (onEnableAndIgnore != null) - { - actionItems.Add(s_enableAndIgnoreItem); - } - - var infoBarModel = new InfoBarModel( - textSpans, - actionItems.ToArray(), - KnownMonikers.StatusInformation, - isCloseButtonVisible: true); - - IVsInfoBarUIElement infoBarUI; - if (!TryCreateInfoBarUI(factory, infoBarModel, out infoBarUI)) - { - return; - } - - uint? infoBarCookie = null; - var eventSink = new CodeFixInfoBarEvents(() => - { - onClose(); - - if (infoBarCookie.HasValue) - { - infoBarUI.Unadvise(infoBarCookie.Value); - } - }, onEnable, onEnableAndIgnore); - - uint cookie; - infoBarUI.Advise(eventSink, out cookie); - infoBarCookie = cookie; - - IVsInfoBarHost host = (IVsInfoBarHost)unknown; - host.AddInfoBar(infoBarUI); - } - private class CodeFixInfoBarEvents : IVsInfoBarUIEvents { private readonly Action _onClose; diff --git a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs index dbbbbb97e5c4f..b2de8cade68ee 100644 --- a/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs +++ b/src/Workspaces/Core/Portable/ExtensionManager/IErrorReportingService.cs @@ -8,7 +8,6 @@ namespace Microsoft.CodeAnalysis.Extensions { internal interface IErrorReportingService : IWorkspaceService { - void ShowErrorInfoForCodeFix(string codefixName, Action OnEnable, Action OnEnableAndIgnore, Action OnClose); void ShowErrorInfo(string message, params ErrorReportingUI[] items); } diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs index 73b2b4928a824..98d3db4511212 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs +++ b/src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs @@ -61,6 +61,15 @@ internal WorkspacesResources() { } } + /// + /// Looks up a localized string similar to '{0}' encountered an error and has been disabled.. + /// + internal static string _0_encountered_an_error_and_has_been_disabled { + get { + return ResourceManager.GetString("_0_encountered_an_error_and_has_been_disabled", resourceCulture); + } + } + /// /// Looks up a localized string similar to '{0}' is already part of the workspace.. /// @@ -530,6 +539,24 @@ internal static string Duplicate_source_file_0_in_project_1 { } } + /// + /// Looks up a localized string similar to Enable. + /// + internal static string Enable { + get { + return ResourceManager.GetString("Enable", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Enable and ignore future errors. + /// + internal static string Enable_and_ignore_future_errors { + get { + return ResourceManager.GetString("Enable_and_ignore_future_errors", resourceCulture); + } + } + /// /// Looks up a localized string similar to Exceptions:. /// diff --git a/src/Workspaces/Core/Portable/WorkspacesResources.resx b/src/Workspaces/Core/Portable/WorkspacesResources.resx index 7bc55e44a44d1..a3c32b0b9d4f8 100644 --- a/src/Workspaces/Core/Portable/WorkspacesResources.resx +++ b/src/Workspaces/Core/Portable/WorkspacesResources.resx @@ -474,4 +474,13 @@ Options did not come from Workspace + + Enable + + + Enable and ignore future errors + + + '{0}' encountered an error and has been disabled. + \ No newline at end of file From d2793f4d5b84d874df750fc6212d4d1eee7af3c2 Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Thu, 4 Aug 2016 18:25:34 -0700 Subject: [PATCH 08/31] add dialog window --- .../EditorLayerExtensionManager.cs | 6 +-- .../Workspaces/EditorErrorReportingService.cs | 5 ++ .../Workspace/DetailedErrorInfoDialog.xaml | 35 ++++++++++++++ .../Workspace/DetailedErrorInfoDialog.xaml.cs | 48 +++++++++++++++++++ .../VisualStudioErrorReportingService.cs | 46 +++++------------- .../Core/Def/PublicAPI.Unshipped.txt | 2 + .../Core/Def/ServicesVSResources.Designer.cs | 18 +++++++ .../Core/Def/ServicesVSResources.resx | 6 +++ .../Core/Def/ServicesVisualStudio.csproj | 7 +++ .../IErrorReportingService.cs | 1 + .../Portable/WorkspacesResources.Designer.cs | 9 ++++ .../Core/Portable/WorkspacesResources.resx | 3 ++ 12 files changed, 149 insertions(+), 37 deletions(-) create mode 100644 src/VisualStudio/Core/Def/Implementation/Workspace/DetailedErrorInfoDialog.xaml create mode 100644 src/VisualStudio/Core/Def/Implementation/Workspace/DetailedErrorInfoDialog.xaml.cs diff --git a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs index 73604b9cce274..8cb52768eebc9 100644 --- a/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs +++ b/src/EditorFeatures/Core/Implementation/EditorLayerExtensionManager.cs @@ -70,7 +70,7 @@ public override void HandleException(object provider, Exception exception) base.HandleException(provider, exception); _errorReportingService?.ShowErrorInfo(String.Format(WorkspacesResources._0_encountered_an_error_and_has_been_disabled, provider.GetType().Name), - new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.HyperLink, () => LaunchExceptionInfoWindow(exception)), + new ErrorReportingUI(WorkspacesResources.Show_Stack_Trace, ErrorReportingUI.UIKind.HyperLink, () => ShowDetailedErrorInfo(exception), closeAfterAction: false), new ErrorReportingUI(WorkspacesResources.Enable, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), new ErrorReportingUI(WorkspacesResources.Enable_and_ignore_future_errors, ErrorReportingUI.UIKind.Button, () => { EnableProvider(provider); LogEnableProvider(provider); }), new ErrorReportingUI(String.Empty, ErrorReportingUI.UIKind.Close, () => LogLeaveDisabled(provider))); @@ -93,9 +93,9 @@ public override void HandleException(object provider, Exception exception) _errorLoggerService?.LogException(provider, exception); } - private void LaunchExceptionInfoWindow(Exception exception) + private void ShowDetailedErrorInfo(Exception exception) { - throw new NotImplementedException(); + _errorReportingService.ShowDetailedErrorInfo(exception); } private static void LogLeaveDisabled(object provider) diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs index 939ffbd6838fe..7a9fb43684a47 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/EditorErrorReportingService.cs @@ -8,6 +8,11 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces { internal class EditorErrorReportingService : IErrorReportingService { + public void ShowDetailedErrorInfo(Exception exception) + { + Logger.Log(FunctionId.Extension_Exception, exception.StackTrace); + } + public void ShowErrorInfo(string message, params ErrorReportingUI[] items) { Logger.Log(FunctionId.Extension_Exception, message); diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/DetailedErrorInfoDialog.xaml b/src/VisualStudio/Core/Def/Implementation/Workspace/DetailedErrorInfoDialog.xaml new file mode 100644 index 0000000000000..39ed5aa69268e --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/DetailedErrorInfoDialog.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + +