From d5562d50cd253c027677a87367e85329f443242c Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Tue, 11 Apr 2017 15:28:32 -0700 Subject: [PATCH 001/214] Don't crash when renaming a method to Finalize (and there's a destructor present) Fixes https://github.com/dotnet/roslyn/issues/16567 --- .../RenameEngineTests.CSharpConflicts.vb | 25 +++++++++++++++++++ .../CSharpRenameRewriterLanguageService.cs | 10 +++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb b/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb index acc436b8b6db9..e7a15b31985d4 100644 --- a/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb +++ b/src/EditorFeatures/Test2/Rename/RenameEngineTests.CSharpConflicts.vb @@ -3585,6 +3585,31 @@ partial class {|current:$$C|} { } result.AssertLabeledSpansAre("current", type:=RelatedLocationType.NoConflict) End Using End Sub + + + + + Public Sub RenameMethodToFinalizeWithDestructorPresent() + Using result = RenameEngineResult.Create(_outputHelper, + + + +class C +{ + ~{|Conflict:C|}() { } + void $$[|M|]() + { + int x = 7; + int y = ~x; + } +} + + + , renameTo:="Finalize") + + result.AssertLabeledSpansAre("Conflict", type:=RelatedLocationType.UnresolvedConflict) + End Using + End Sub End Class End Namespace diff --git a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs index 3acf0e9251774..7ab27cb27c6d7 100644 --- a/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs +++ b/src/Workspaces/CSharp/Portable/Rename/CSharpRenameRewriterLanguageService.cs @@ -215,7 +215,8 @@ public override SyntaxToken VisitToken(SyntaxToken token) isRenameLocation || token.ValueText == _replacementText || isOldText || - _possibleNameConflicts.Contains(token.ValueText); + _possibleNameConflicts.Contains(token.ValueText) || + IsPossiblyDestructorConflict(token, _replacementText); if (tokenNeedsConflictCheck) { @@ -230,6 +231,13 @@ public override SyntaxToken VisitToken(SyntaxToken token) return newToken; } + private bool IsPossiblyDestructorConflict(SyntaxToken token, string replacementText) + { + return _replacementText == "Finalize" && + token.IsKind(SyntaxKind.IdentifierToken) && + token.Parent.IsKind(SyntaxKind.DestructorDeclaration); + } + private SyntaxNode Complexify(SyntaxNode originalNode, SyntaxNode newNode) { _isProcessingComplexifiedSpans = true; From 6d81f4e3bbe4a45ed897a5de8d29959a5259cbc9 Mon Sep 17 00:00:00 2001 From: Kenny Nygaard Date: Sat, 8 Apr 2017 14:41:47 -0600 Subject: [PATCH 002/214] Fixes #18556 --- .../ImplementInterfaceTests.cs | 50 +++++++++++++++++++ ...actImplementInterfaceService.CodeAction.cs | 37 +++++++++++--- 2 files changed, 80 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs index 8c65728e7fc13..36b96e39fda56 100644 --- a/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs +++ b/src/EditorFeatures/CSharpTest/ImplementInterface/ImplementInterfaceTests.cs @@ -2350,6 +2350,56 @@ public int M2() index: 1); } + [WorkItem(18556, "https://github.com/dotnet/roslyn/issues/18556")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] + public async Task TestImplementInterfaceThroughExplicitProperty() + { + await TestActionCountAsync( +@"interface IA +{ + IB B { get; } +} +interface IB +{ + int M(); +} +class AB : IA, [|IB|] +{ + IB IA.B => null; +}", +count: 3); + await TestWithAllCodeStyleOptionsOffAsync( +@"interface IA +{ + IB B { get; } +} +interface IB +{ + int M(); +} +class AB : IA, [|IB|] +{ + IB IA.B => null; +}", +@"interface IA +{ + IB B { get; } +} +interface IB +{ + int M(); +} +class AB : IA, [|IB|] +{ + IB IA.B => null; + + public int M() + { + return ((IA)this).B.M(); + } +}", index: 1); + } + [WorkItem(768799, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/768799")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementInterface)] public async Task TestNoImplementThroughIndexer() diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index 7c3bcba99af70..5525d0f4610d7 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -159,8 +159,8 @@ protected override Task GetChangedDocumentAsync(CancellationToken canc public Task GetUpdatedDocumentAsync(CancellationToken cancellationToken) { - var unimplementedMembers = Explicitly - ? State.UnimplementedExplicitMembers + var unimplementedMembers = Explicitly + ? State.UnimplementedExplicitMembers : State.UnimplementedMembers; return GetUpdatedDocumentAsync(Document, unimplementedMembers, State.ClassOrStructType, State.ClassOrStructDecl, cancellationToken); } @@ -476,13 +476,36 @@ private SyntaxNode CreateThroughExpression(SyntaxGenerator factory) // uncommon case and optimize for the common one - in other words, we only apply the cast // in cases where we can unambiguously figure out which interface we are trying to implement. var interfaceBeingImplemented = State.InterfaceTypes.SingleOrDefault(); - if ((interfaceBeingImplemented != null) && (!throughMemberType.Equals(interfaceBeingImplemented))) + if ((interfaceBeingImplemented != null)) { - through = factory.CastExpression(interfaceBeingImplemented, - through.WithAdditionalAnnotations(Simplifier.Annotation)); + if (!throughMemberType.Equals(interfaceBeingImplemented)) + { + through = factory.CastExpression(interfaceBeingImplemented, + through.WithAdditionalAnnotations(Simplifier.Annotation)); + + var facts = this.Document.GetLanguageService(); + through = facts.Parenthesize(through); + } + else if (!ThroughMember.IsStatic && ThroughMember is IPropertySymbol throughMemberProperty && throughMemberProperty.ExplicitInterfaceImplementations.Any()) + { + // If we are implementing through an explicitly implemented property, we need to cast 'this' to + // the explicitly implemented property type before calling the member, as in: + // ((IA)this).Prop.Member(); + // + var explicitlyImplementedProperty = throughMemberProperty.ExplicitInterfaceImplementations[0]; - var facts = this.Document.GetLanguageService(); - through = facts.Parenthesize(through); + var explicitImplementationCast = factory.CastExpression( + explicitlyImplementedProperty.ContainingType, + factory.ThisExpression()); + + var facts = this.Document.GetLanguageService(); + explicitImplementationCast = facts.Parenthesize(explicitImplementationCast); + + through = factory.MemberAccessExpression(explicitImplementationCast, + factory.IdentifierName(explicitlyImplementedProperty.Name)) + .WithAdditionalAnnotations(Simplifier.Annotation); + + } } } From 77259683bf2311f4ead16694caa0b3366df7b6d5 Mon Sep 17 00:00:00 2001 From: Kenny Nygaard Date: Mon, 10 Apr 2017 21:12:37 -0600 Subject: [PATCH 003/214] Changes per PR review --- ...actImplementInterfaceService.CodeAction.cs | 31 ++++++++++--------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index 5525d0f4610d7..c04e0e9782802 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -441,14 +441,14 @@ private IMethodSymbol GetAddOrRemoveMethod(bool generateInvisibly, return generateInvisibly ? accessor : null; } - private SyntaxNode CreateThroughExpression(SyntaxGenerator factory) + private SyntaxNode CreateThroughExpression(SyntaxGenerator generator) { var through = ThroughMember.IsStatic - ? GenerateName(factory, State.ClassOrStructType.IsGenericType) - : factory.ThisExpression(); + ? GenerateName(generator, State.ClassOrStructType.IsGenericType) + : generator.ThisExpression(); - through = factory.MemberAccessExpression( - through, factory.IdentifierName(ThroughMember.Name)); + through = generator.MemberAccessExpression( + through, generator.IdentifierName(ThroughMember.Name)); var throughMemberType = ThroughMember.GetMemberType(); if ((State.InterfaceTypes != null) && (throughMemberType != null)) @@ -476,34 +476,37 @@ private SyntaxNode CreateThroughExpression(SyntaxGenerator factory) // uncommon case and optimize for the common one - in other words, we only apply the cast // in cases where we can unambiguously figure out which interface we are trying to implement. var interfaceBeingImplemented = State.InterfaceTypes.SingleOrDefault(); - if ((interfaceBeingImplemented != null)) + if (interfaceBeingImplemented != null) { if (!throughMemberType.Equals(interfaceBeingImplemented)) { - through = factory.CastExpression(interfaceBeingImplemented, + through = generator.CastExpression(interfaceBeingImplemented, through.WithAdditionalAnnotations(Simplifier.Annotation)); var facts = this.Document.GetLanguageService(); through = facts.Parenthesize(through); } - else if (!ThroughMember.IsStatic && ThroughMember is IPropertySymbol throughMemberProperty && throughMemberProperty.ExplicitInterfaceImplementations.Any()) + else if (!ThroughMember.IsStatic && + ThroughMember is IPropertySymbol throughMemberProperty && + throughMemberProperty.ExplicitInterfaceImplementations.Any()) { // If we are implementing through an explicitly implemented property, we need to cast 'this' to - // the explicitly implemented property type before calling the member, as in: + // the explicitly implemented interface type before calling the member, as in: // ((IA)this).Prop.Member(); // var explicitlyImplementedProperty = throughMemberProperty.ExplicitInterfaceImplementations[0]; - var explicitImplementationCast = factory.CastExpression( + var explicitImplementationCast = generator.CastExpression( explicitlyImplementedProperty.ContainingType, - factory.ThisExpression()); + generator.ThisExpression()); var facts = this.Document.GetLanguageService(); explicitImplementationCast = facts.Parenthesize(explicitImplementationCast); - through = factory.MemberAccessExpression(explicitImplementationCast, - factory.IdentifierName(explicitlyImplementedProperty.Name)) - .WithAdditionalAnnotations(Simplifier.Annotation); + through = generator.MemberAccessExpression(explicitImplementationCast, + generator.IdentifierName(explicitlyImplementedProperty.Name)); + + through = through.WithAdditionalAnnotations(Simplifier.Annotation); } } From b7be2451d4a72fae32c09dc65752ffbeb0948eab Mon Sep 17 00:00:00 2001 From: Kenny Nygaard Date: Thu, 13 Apr 2017 14:39:00 -0600 Subject: [PATCH 004/214] Remove unnecessary parenthesize --- .../AbstractImplementInterfaceService.CodeAction.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index c04e0e9782802..d408831bf005b 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -499,10 +499,7 @@ ThroughMember is IPropertySymbol throughMemberProperty && var explicitImplementationCast = generator.CastExpression( explicitlyImplementedProperty.ContainingType, generator.ThisExpression()); - - var facts = this.Document.GetLanguageService(); - explicitImplementationCast = facts.Parenthesize(explicitImplementationCast); - + through = generator.MemberAccessExpression(explicitImplementationCast, generator.IdentifierName(explicitlyImplementedProperty.Name)); From 7b2ecf127c61194333d3438990b2eca4a0e7c883 Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Thu, 13 Apr 2017 15:51:25 -0700 Subject: [PATCH 005/214] Notify XAML of renames caused by Naming Rule fixes Fixes https://github.com/dotnet/roslyn/issues/16562 --- .../ServicesTestUtilities.csproj | 1 + ...eActionOperationFactoryWorkspaceService.cs | 34 ++++++++ .../AbstractNamingStyleCodeFixProvider.cs | 52 +++++++++++- ...eActionOperationFactoryWorkspaceService.cs | 11 +++ src/Features/Core/Portable/Features.csproj | 1 + ...eActionOperationFactoryWorkspaceService.cs | 80 +++++++++++++++++++ .../Core/Def/ServicesVisualStudio.csproj | 1 + 7 files changed, 176 insertions(+), 4 deletions(-) create mode 100644 src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs create mode 100644 src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs create mode 100644 src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs diff --git a/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj b/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj index 3026d33cb55fd..79f5349d94819 100644 --- a/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj +++ b/src/EditorFeatures/TestUtilities/ServicesTestUtilities.csproj @@ -233,6 +233,7 @@ + diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs new file mode 100644 index 0000000000000..037a8bf340c34 --- /dev/null +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs @@ -0,0 +1,34 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces +{ + [ExportWorkspaceService(typeof(ISymbolRenamedCodeActionOperationFactoryWorkspaceService), TestWorkspace.WorkspaceName), Shared] + public class TestSymbolRenamedCodeActionOperationFactoryWorkspaceService : ISymbolRenamedCodeActionOperationFactoryWorkspaceService + { + public CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution) + { + return new Operation(symbol, newName, startingSolution, updatedSolution); + } + + public class Operation : CodeActionOperation + { + private ISymbol _symbol; + private string _newName; + private Solution _startingSolution; + private Solution _updatedSolution; + + public Operation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution) + { + _symbol = symbol; + _newName = newName; + _startingSolution = startingSolution; + _updatedSolution = updatedSolution; + } + } + } +} diff --git a/src/Features/Core/Portable/CodeFixes/NamingStyle/AbstractNamingStyleCodeFixProvider.cs b/src/Features/Core/Portable/CodeFixes/NamingStyle/AbstractNamingStyleCodeFixProvider.cs index b5b902eeb1e5e..e0212f23dc80b 100644 --- a/src/Features/Core/Portable/CodeFixes/NamingStyle/AbstractNamingStyleCodeFixProvider.cs +++ b/src/Features/Core/Portable/CodeFixes/NamingStyle/AbstractNamingStyleCodeFixProvider.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.Collections.Immutable; using System.Composition; using System.Linq; @@ -8,6 +9,7 @@ using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.NamingStyles; using Microsoft.CodeAnalysis.Rename; @@ -52,9 +54,12 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) var solution = context.Document.Project.Solution; context.RegisterCodeFix( new FixNameCodeAction( + solution, + symbol, + fixedName, string.Format(FeaturesResources.Fix_Name_Violation_colon_0, fixedName), c => FixAsync(document, symbol, fixedName, c), - nameof(NamingStyleCodeFixProvider)), + equivalenceKey: nameof(NamingStyleCodeFixProvider)), diagnostic); } } @@ -68,12 +73,51 @@ await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } - private class FixNameCodeAction : CodeAction.SolutionChangeAction + private class FixNameCodeAction : CodeAction { - public FixNameCodeAction(string title, Func> createChangedSolution, string equivalenceKey) - : base(title, createChangedSolution, equivalenceKey) + private readonly Solution _startingSolution; + private readonly ISymbol _symbol; + private readonly string _newName; + private readonly string _title; + private readonly Func> _createChangedSolutionAsync; + private readonly string _equivalenceKey; + + public FixNameCodeAction( + Solution startingSolution, + ISymbol symbol, + string newName, + string title, + Func> createChangedSolutionAsync, + string equivalenceKey) { + _startingSolution = startingSolution; + _symbol = symbol; + _newName = newName; + _title = title; + _createChangedSolutionAsync = createChangedSolutionAsync; + _equivalenceKey = equivalenceKey; } + + protected override async Task> ComputePreviewOperationsAsync(CancellationToken cancellationToken) + { + return SpecializedCollections.SingletonEnumerable( + new ApplyChangesOperation(await _createChangedSolutionAsync(cancellationToken).ConfigureAwait(false))); + } + + protected override async Task> ComputeOperationsAsync(CancellationToken cancellationToken) + { + var factory =_startingSolution.Workspace.Services.GetService(); + var newSolution = await _createChangedSolutionAsync(cancellationToken).ConfigureAwait(false); + return new CodeActionOperation[] + { + new ApplyChangesOperation(newSolution), + factory.CreateSymbolRenamedOperation(_symbol, _newName, _startingSolution, newSolution) + }.AsEnumerable(); + } + + public override string Title => _title; + + public override string EquivalenceKey => _equivalenceKey; } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs new file mode 100644 index 0000000000000..67e52d65bb1d8 --- /dev/null +++ b/src/Features/Core/Portable/CodeRefactorings/WorkspaceServices/ISymbolRenamedCodeActionOperationFactoryWorkspaceService.cs @@ -0,0 +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 Microsoft.CodeAnalysis.Host; + +namespace Microsoft.CodeAnalysis.CodeActions.WorkspaceServices +{ + internal interface ISymbolRenamedCodeActionOperationFactoryWorkspaceService : IWorkspaceService + { + CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution); + } +} diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 8bfa7d0ca6f60..38f5e4129410f 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -112,6 +112,7 @@ + diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs new file mode 100644 index 0000000000000..264ac8171dc87 --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs @@ -0,0 +1,80 @@ +// 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.Composition; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeActions.WorkspaceServices; +using Microsoft.CodeAnalysis.Editor; +using Microsoft.CodeAnalysis.Host.Mef; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation +{ + [ExportWorkspaceService(typeof(ISymbolRenamedCodeActionOperationFactoryWorkspaceService), ServiceLayer.Host), Shared] + internal sealed class VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService : ISymbolRenamedCodeActionOperationFactoryWorkspaceService + { + private readonly IEnumerable _refactorNotifyServices; + + [ImportingConstructor] + public VisualStudioSymbolRenamedCodeActionOperationFactoryWorkspaceService( + [ImportMany] IEnumerable refactorNotifyServices) + { + _refactorNotifyServices = refactorNotifyServices; + } + + public CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution) + { + return new RenameSymbolOperation( + _refactorNotifyServices, + symbol ?? throw new ArgumentNullException(nameof(symbol)), + newName ?? throw new ArgumentNullException(nameof(newName)), + startingSolution ?? throw new ArgumentNullException(nameof(startingSolution)), + updatedSolution ?? throw new ArgumentNullException(nameof(updatedSolution))); + } + + private class RenameSymbolOperation : CodeActionOperation + { + private readonly IEnumerable _refactorNotifyServices; + private readonly ISymbol _symbol; + private readonly string _newName; + private readonly Solution _startingSolution; + private readonly Solution _updatedSolution; + + public RenameSymbolOperation( + IEnumerable refactorNotifyServices, + ISymbol symbol, + string newName, + Solution startingSolution, + Solution updatedSolution) + { + _refactorNotifyServices = refactorNotifyServices; + _symbol = symbol; + _newName = newName; + _startingSolution = startingSolution; + _updatedSolution = updatedSolution; + } + + public override void Apply(Workspace workspace, CancellationToken cancellationToken = default(CancellationToken)) + { + var updatedDocumentIds = _updatedSolution.GetChanges(_startingSolution).GetProjectChanges().SelectMany(p => p.GetChangedDocuments()); + + foreach (var refactorNotifyService in _refactorNotifyServices) + { + // If something goes wrong and some language service rejects the rename, we + // can't really do anything about it because we're potentially in the middle of + // some unknown set of CodeActionOperations. This is a best effort approach. + + if (refactorNotifyService.TryOnBeforeGlobalSymbolRenamed(workspace, updatedDocumentIds, _symbol, _newName, throwOnFailure: false)) + { + refactorNotifyService.TryOnAfterGlobalSymbolRenamed(workspace, updatedDocumentIds, _symbol, _newName, throwOnFailure: false); + } + } + } + + public override string Title => string.Format(EditorFeaturesResources.Rename_0_to_1, _symbol.Name, _newName); + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 19480510cffe1..4deee1c99dc24 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -640,6 +640,7 @@ + From 032548f2ce7c8cb857b15762936f6df07975994f Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Wed, 19 Apr 2017 11:27:01 -0700 Subject: [PATCH 006/214] Add test to show the new Operation is included --- .../NamingStyles/NamingStylesTests.cs | 25 +++++++++++++++++++ ...eActionOperationFactoryWorkspaceService.cs | 8 +++--- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs index bd8d373b97b57..b35cae01cad33 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.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.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeFixes.NamingStyles; using Microsoft.CodeAnalysis.CSharp.Diagnostics.NamingStyles; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Roslyn.Test.Utilities; using Xunit; @@ -306,5 +308,28 @@ class D : C internal override void [|m|]() { } }", new TestParameters(options: MethodNamesArePascalCase)); } + + [Fact, Trait(Traits.Feature, Traits.Features.NamingStyle)] + [WorkItem(16562, "https://github.com/dotnet/roslyn/issues/16562")] + public async Task TestRefactorNotify() + { + var markup = @"public class [|c|] { }"; + var testParameters = new TestParameters(options: ClassNamesArePascalCase); + + using (var workspace = CreateWorkspaceFromOptions(markup, testParameters)) + { + var actions = await GetCodeActionsAsync(workspace, testParameters); + + var previewOperations = await actions[0].GetPreviewOperationsAsync(CancellationToken.None); + Assert.Empty(previewOperations.OfType()); + + var commitOperations = await actions[0].GetOperationsAsync(CancellationToken.None); + Assert.Equal(2, commitOperations.Length); + + var symbolRenamedOperation = (TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.Operation)commitOperations[1]; + Assert.Equal("c", symbolRenamedOperation._symbol.Name); + Assert.Equal("C", symbolRenamedOperation._newName); + } + } } } \ No newline at end of file diff --git a/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs b/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs index 037a8bf340c34..cbd71aef192f9 100644 --- a/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs +++ b/src/EditorFeatures/TestUtilities/Workspaces/TestSymbolRenamedCodeActionOperationFactoryWorkspaceService.cs @@ -17,10 +17,10 @@ public CodeActionOperation CreateSymbolRenamedOperation(ISymbol symbol, string n public class Operation : CodeActionOperation { - private ISymbol _symbol; - private string _newName; - private Solution _startingSolution; - private Solution _updatedSolution; + public ISymbol _symbol; + public string _newName; + public Solution _startingSolution; + public Solution _updatedSolution; public Operation(ISymbol symbol, string newName, Solution startingSolution, Solution updatedSolution) { From d3d1860a32c080eb3b84da3f446eabd6144ab93e Mon Sep 17 00:00:00 2001 From: Kenny Nygaard Date: Thu, 20 Apr 2017 17:42:05 -0600 Subject: [PATCH 007/214] PR feedback --- .../AbstractImplementInterfaceService.CodeAction.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs index d408831bf005b..7ac0497e2c893 100644 --- a/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs +++ b/src/Features/Core/Portable/ImplementInterface/AbstractImplementInterfaceService.CodeAction.cs @@ -482,9 +482,6 @@ private SyntaxNode CreateThroughExpression(SyntaxGenerator generator) { through = generator.CastExpression(interfaceBeingImplemented, through.WithAdditionalAnnotations(Simplifier.Annotation)); - - var facts = this.Document.GetLanguageService(); - through = facts.Parenthesize(through); } else if (!ThroughMember.IsStatic && ThroughMember is IPropertySymbol throughMemberProperty && @@ -504,7 +501,6 @@ ThroughMember is IPropertySymbol throughMemberProperty && generator.IdentifierName(explicitlyImplementedProperty.Name)); through = through.WithAdditionalAnnotations(Simplifier.Annotation); - } } } From 104499d9fe7e62f85d784fe3e2c17b6360578cd7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 22 Apr 2017 15:27:59 -0500 Subject: [PATCH 008/214] Eliminate delegate allocations in PooledObject.Create --- .../Utilities/ObjectPools/PooledObject.cs | 30 +++++++++++++++---- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs b/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs index 005c6719204ce..f0e5d5bd071cf 100644 --- a/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs +++ b/src/Workspaces/Core/Portable/Utilities/ObjectPools/PooledObject.cs @@ -36,32 +36,50 @@ public void Dispose() #region factory public static PooledObject Create(ObjectPool pool) { - return new PooledObject(pool, Allocator, Releaser); + return new PooledObject( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) { - return new PooledObject>(pool, Allocator, Releaser); + return new PooledObject>( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) { - return new PooledObject>(pool, Allocator, Releaser); + return new PooledObject>( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) { - return new PooledObject>(pool, Allocator, Releaser); + return new PooledObject>( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) { - return new PooledObject>(pool, Allocator, Releaser); + return new PooledObject>( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } public static PooledObject> Create(ObjectPool> pool) { - return new PooledObject>(pool, Allocator, Releaser); + return new PooledObject>( + pool, + p => Allocator(p), + (p, sb) => Releaser(p, sb)); } #endregion From f39da9dd1401df7caa3f4a76bf91301668a7cb9d Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 28 Mar 2017 08:03:31 -0500 Subject: [PATCH 009/214] Expand refactoring test cases to cover docs for generated setters --- .../ReplacePropertyWithMethodsTests.cs | 20 ++++++++++++------- .../ReplacePropertyWithMethodsTests.vb | 18 +++++++++++------ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs index 2fe206b531079..1ae233cd3175f 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs @@ -1363,7 +1363,7 @@ await TestInRegularAndScriptAsync( @"internal interface ILanguageServiceHost { /// - /// Gets the active workspace project context that provides access to the language service for the active configured project. + /// Sets the active workspace project context that provides access to the language service for the active configured project. /// /// /// An value that provides access to the language service for the active configured project. @@ -1376,11 +1376,11 @@ object [||]ActiveProjectContext @"internal interface ILanguageServiceHost { /// - /// Gets the active workspace project context that provides access to the language service for the active configured project. + /// Sets the active workspace project context that provides access to the language service for the active configured project. /// - /// + /// /// An value that provides access to the language service for the active configured project. - /// + /// void SetActiveProjectContext(object value); }", ignoreTrivia: false); } @@ -1393,7 +1393,7 @@ await TestInRegularAndScriptAsync( @"internal interface ILanguageServiceHost { /// - /// Gets the active workspace project context that provides access to the language service for the active configured project. + /// Gets or sets the active workspace project context that provides access to the language service for the active configured project. /// /// /// An value that provides access to the language service for the active configured project. @@ -1406,12 +1406,18 @@ object [||]ActiveProjectContext @"internal interface ILanguageServiceHost { /// - /// Gets the active workspace project context that provides access to the language service for the active configured project. + /// Gets or sets the active workspace project context that provides access to the language service for the active configured project. /// /// /// An value that provides access to the language service for the active configured project. /// object GetActiveProjectContext(); + /// + /// Gets or sets the active workspace project context that provides access to the language service for the active configured project. + /// + /// + /// An value that provides access to the language service for the active configured project. + /// void SetActiveProjectContext(object value); }", ignoreTrivia: false); } @@ -1419,4 +1425,4 @@ object [||]ActiveProjectContext private IDictionary PreferExpressionBodiedMethods => OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement)); } -} \ No newline at end of file +} diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb index 7b5de5152a9cb..82d38ccd76182 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb @@ -445,7 +445,7 @@ End Interface", ignoreTrivia:=False) Await TestInRegularAndScriptAsync( "Interface ILanguageServiceHost ''' - ''' Gets the active workspace project context that provides access to the language service for the active configured project. + ''' Sets the active workspace project context that provides access to the language service for the active configured project. ''' ''' ''' An that provides access to the language service for the active configured project. @@ -454,11 +454,11 @@ End Interface", ignoreTrivia:=False) End Interface", "Interface ILanguageServiceHost ''' - ''' Gets the active workspace project context that provides access to the language service for the active configured project. + ''' Sets the active workspace project context that provides access to the language service for the active configured project. ''' - ''' + ''' ''' An that provides access to the language service for the active configured project. - ''' + ''' Sub SetActiveProjectContext(Value As Object) End Interface", ignoreTrivia:=False) End Function @@ -469,7 +469,7 @@ End Interface", ignoreTrivia:=False) Await TestInRegularAndScriptAsync( "Interface ILanguageServiceHost ''' - ''' Gets the active workspace project context that provides access to the language service for the active configured project. + ''' Gets or sets the active workspace project context that provides access to the language service for the active configured project. ''' ''' ''' An that provides access to the language service for the active configured project. @@ -478,12 +478,18 @@ End Interface", ignoreTrivia:=False) End Interface", "Interface ILanguageServiceHost ''' - ''' Gets the active workspace project context that provides access to the language service for the active configured project. + ''' Gets or sets the active workspace project context that provides access to the language service for the active configured project. ''' ''' ''' An that provides access to the language service for the active configured project. ''' Function GetActiveProjectContext() As Object + ''' + ''' Gets or sets the active workspace project context that provides access to the language service for the active configured project. + ''' + ''' + ''' An that provides access to the language service for the active configured project. + ''' Sub SetActiveProjectContext(Value As Object) End Interface", ignoreTrivia:=False) End Function From 1b7b72de18e98d4a7f1ab3eca20586b1e83945ce Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 28 Mar 2017 08:21:13 -0500 Subject: [PATCH 010/214] Expand refactoring test cases to cover cref attributes --- .../ReplacePropertyWithMethodsTests.cs | 109 ++++++++++++++++++ .../ReplacePropertyWithMethodsTests.vb | 89 +++++++++++++- 2 files changed, 195 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs index 1ae233cd3175f..8a8cc2a6c363d 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs @@ -1412,6 +1412,7 @@ object [||]ActiveProjectContext /// An value that provides access to the language service for the active configured project. /// object GetActiveProjectContext(); + /// /// Gets or sets the active workspace project context that provides access to the language service for the active configured project. /// @@ -1422,6 +1423,114 @@ object [||]ActiveProjectContext }", ignoreTrivia: false); } + [WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestDocumentationComment4() + { + await TestInRegularAndScriptAsync( +@"internal interface ILanguageServiceHost +{ + /// + /// Sets . + /// + /// + object [||]ActiveProjectContext + { + set; + } +} +internal struct AStruct +{ + /// + private int x; +}", +@"internal interface ILanguageServiceHost +{ + /// + /// Sets . + /// + /// + void SetActiveProjectContext(object value); +} +internal struct AStruct +{ + /// + private int x; +}", ignoreTrivia: false); + } + + [WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestDocumentationComment5() + { + await TestInRegularAndScriptAsync( +@"internal interface ILanguageServiceHost +{ + /// + /// Gets or sets . + /// + /// + object [||]ActiveProjectContext + { + get; set; + } +} +internal struct AStruct +{ + /// + private int x; +}", +@"internal interface ILanguageServiceHost +{ + /// + /// Gets or sets . + /// + /// + object GetActiveProjectContext(); + + /// + /// Gets or sets . + /// + /// + void SetActiveProjectContext(object value); +} +internal struct AStruct +{ + /// + private int x; +}", ignoreTrivia: false); + } + + [WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestDocumentationComment6() + { + await TestInRegularAndScriptAsync( +@"internal interface ISomeInterface +{ + /// + ISomeInterface [||]Context + { + set; + } +} +internal struct AStruct +{ + /// + private int x; +}", +@"internal interface ISomeInterface +{ + /// + void SetContext(ISomeInterface value); +} +internal struct AStruct +{ + /// + private int x; +}", ignoreTrivia: false); + } + private IDictionary PreferExpressionBodiedMethods => OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement)); } diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb index 82d38ccd76182..1665a8a472717 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb @@ -415,7 +415,7 @@ end class", end class") End Function - + Public Async Function TestDocumentationComment1() As Task Await TestInRegularAndScriptAsync( @@ -439,7 +439,7 @@ End Interface", End Interface", ignoreTrivia:=False) End Function - + Public Async Function TestDocumentationComment2() As Task Await TestInRegularAndScriptAsync( @@ -463,7 +463,7 @@ End Interface", End Interface", ignoreTrivia:=False) End Function - + Public Async Function TestDocumentationComment3() As Task Await TestInRegularAndScriptAsync( @@ -493,5 +493,88 @@ End Interface", Sub SetActiveProjectContext(Value As Object) End Interface", ignoreTrivia:=False) End Function + + + + Public Async Function TestDocumentationComment4() As Task + Await TestInRegularAndScriptAsync( +"Interface ILanguageServiceHost + ''' + ''' Sets . + ''' + ''' + WriteOnly Property [||]ActiveProjectContext As Object +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", +"Interface ILanguageServiceHost + ''' + ''' Sets . + ''' + ''' + Sub SetActiveProjectContext(Value As Object) +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", ignoreTrivia:=False) + End Function + + + + Public Async Function TestDocumentationComment5() As Task + Await TestInRegularAndScriptAsync( +"Interface ILanguageServiceHost + ''' + ''' Gets or sets . + ''' + ''' + Property [||]ActiveProjectContext As Object +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", +"Interface ILanguageServiceHost + ''' + ''' Gets or sets . + ''' + ''' + Function GetActiveProjectContext() As Object + ''' + ''' Gets or sets . + ''' + ''' + Sub SetActiveProjectContext(Value As Object) +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", ignoreTrivia:=False) + End Function + + + + Public Async Function TestDocumentationComment6() As Task + Await TestInRegularAndScriptAsync( +"Interface ISomeInterface(Of T) + ''' + WriteOnly Property [||]Context As ISomeInterface(Of T) +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", +"Interface ISomeInterface(Of T) + ''' + Sub SetContext(Value As ISomeInterface(Of T)) +End Interface +Structure AStruct + ''' + Private X As Integer +End Structure", ignoreTrivia:=False) + End Function End Class End Namespace \ No newline at end of file From a0b31ea48d7aa732b26be3cb89037bd6d06a1ed4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 28 Mar 2017 13:04:37 -0500 Subject: [PATCH 011/214] Fix property to method conversion handling of documentation comments --- .../CSharp/Portable/CSharpFeatures.csproj | 3 +- ...hodsService.ConvertValueToParamRewriter.cs | 39 +++++++++++ ...sService.ConvertValueToReturnsRewriter.cs} | 0 ...CSharpReplacePropertyWithMethodsService.cs | 66 +++++++++++++------ ...stractReplacePropertyWithMethodsService.cs | 45 +++++++++++-- ...pertyWithMethodsCodeRefactoringProvider.cs | 2 +- .../VisualBasic/Portable/BasicFeatures.vbproj | 3 +- ...WithMethods.ConvertValueToParamRewriter.vb | 44 +++++++++++++ ...hMethods.ConvertValueToReturnsRewriter.vb} | 2 +- .../VisualBasicReplacePropertyWithMethods.vb | 56 +++++++++++----- 10 files changed, 215 insertions(+), 45 deletions(-) create mode 100644 src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs rename src/Features/CSharp/Portable/ReplacePropertyWithMethods/{ConvertValueToReturnsRewriter.cs => CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs} (100%) create mode 100644 src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb rename src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/{ConvertValueToReturnsRewriter.vb => VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb} (96%) diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 653a38493d46f..52cc67c931c21 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -86,7 +86,6 @@ - @@ -454,6 +453,8 @@ + + diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs new file mode 100644 index 0000000000000..7f1d5ffa06e37 --- /dev/null +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs @@ -0,0 +1,39 @@ +// 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.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods +{ + internal partial class CSharpReplacePropertyWithMethodsService + { + private class ConvertValueToParamRewriter : CSharpSyntaxRewriter + { + public static readonly CSharpSyntaxRewriter Instance = new ConvertValueToParamRewriter(); + + private ConvertValueToParamRewriter() + { + } + + private XmlNameSyntax ConvertToParam(XmlNameSyntax name) + => name.ReplaceToken(name.LocalName, SyntaxFactory.Identifier("param")); + + private static bool IsValueName(XmlNameSyntax name) + => name.Prefix == null && + name.LocalName.ValueText == "value"; + + public override SyntaxNode VisitXmlElementStartTag(XmlElementStartTagSyntax node) + { + if (!IsValueName(node.Name)) + return base.VisitXmlElementStartTag(node); + + return node.ReplaceNode(node.Name, ConvertToParam(node.Name)) + .AddAttributes(SyntaxFactory.XmlNameAttribute("value")); + } + + public override SyntaxNode VisitXmlElementEndTag(XmlElementEndTagSyntax node) + => IsValueName(node.Name) + ? node.ReplaceNode(node.Name, ConvertToParam(node.Name)) + : base.VisitXmlElementEndTag(node); + } + } +} diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/ConvertValueToReturnsRewriter.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs similarity index 100% rename from src/Features/CSharp/Portable/ReplacePropertyWithMethods/ConvertValueToReturnsRewriter.cs rename to src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs index f47268dd13f8c..e99e7ee6b3500 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs @@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods { [ExportLanguageService(typeof(IReplacePropertyWithMethodsService), LanguageNames.CSharp), Shared] internal partial class CSharpReplacePropertyWithMethodsService : - AbstractReplacePropertyWithMethodsService + AbstractReplacePropertyWithMethodsService { public override SyntaxNode GetPropertyDeclaration(SyntaxToken token) { @@ -98,20 +98,16 @@ private List ConvertPropertyToMembers( documentOptions, parseOptions, generator, propertyDeclaration, propertyBackingField, getMethod, desiredGetMethodName, - copyLeadingTrivia: true, cancellationToken: cancellationToken)); } var setMethod = property.SetMethod; if (setMethod != null) { - // Set-method only gets the leading trivia of the property if we didn't copy - // that trivia to the get-method. result.Add(GetSetMethod( documentOptions, parseOptions, generator, propertyDeclaration, propertyBackingField, setMethod, desiredSetMethodName, - copyLeadingTrivia: getMethod == null, cancellationToken: cancellationToken)); } @@ -126,14 +122,13 @@ private static SyntaxNode GetSetMethod( IFieldSymbol propertyBackingField, IMethodSymbol setMethod, string desiredSetMethodName, - bool copyLeadingTrivia, CancellationToken cancellationToken) { var methodDeclaration = GetSetMethodWorker( generator, propertyDeclaration, propertyBackingField, setMethod, desiredSetMethodName, cancellationToken); - methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, copyLeadingTrivia); + methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, ConvertValueToParamRewriter.Instance); return UseExpressionOrBlockBodyIfDesired( documentOptions, parseOptions, methodDeclaration, @@ -182,14 +177,13 @@ private static SyntaxNode GetGetMethod( IFieldSymbol propertyBackingField, IMethodSymbol getMethod, string desiredGetMethodName, - bool copyLeadingTrivia, CancellationToken cancellationToken) { var methodDeclaration = GetGetMethodWorker( generator, propertyDeclaration, propertyBackingField, getMethod, desiredGetMethodName, cancellationToken); - methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, copyLeadingTrivia); + methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, ConvertValueToReturnsRewriter.Instance); return UseExpressionOrBlockBodyIfDesired( documentOptions, parseOptions, methodDeclaration, @@ -199,32 +193,27 @@ private static SyntaxNode GetGetMethod( private static MethodDeclarationSyntax CopyLeadingTrivia( PropertyDeclarationSyntax propertyDeclaration, MethodDeclarationSyntax methodDeclaration, - bool copyLeadingTrivia) + CSharpSyntaxRewriter documentationCommentRewriter) { - if (copyLeadingTrivia) - { - var leadingTrivia = propertyDeclaration.GetLeadingTrivia(); - return methodDeclaration.WithLeadingTrivia(leadingTrivia.Select(ConvertTrivia)); - } - - return methodDeclaration; + var leadingTrivia = propertyDeclaration.GetLeadingTrivia(); + return methodDeclaration.WithLeadingTrivia(leadingTrivia.Select(trivia => ConvertTrivia(trivia, documentationCommentRewriter))); } - private static SyntaxTrivia ConvertTrivia(SyntaxTrivia trivia) + private static SyntaxTrivia ConvertTrivia(SyntaxTrivia trivia, CSharpSyntaxRewriter rewriter) { if (trivia.Kind() == SyntaxKind.MultiLineDocumentationCommentTrivia || trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia) { - return ConvertDocumentationComment(trivia); + return ConvertDocumentationComment(trivia, rewriter); } return trivia; } - private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia) + private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia, CSharpSyntaxRewriter rewriter) { var structure = trivia.GetStructure(); - var updatedStructure = (StructuredTriviaSyntax)ConvertValueToReturnsRewriter.Instance.Visit(structure); + var updatedStructure = (StructuredTriviaSyntax)rewriter.Visit(structure); return SyntaxFactory.Trivia(updatedStructure); } @@ -305,6 +294,41 @@ public override SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclarati return propertyDeclaration; } + protected override NameMemberCrefSyntax TryGetCrefSyntax(IdentifierNameSyntax identifierName) + { + return identifierName.Parent as NameMemberCrefSyntax; + } + + protected override NameMemberCrefSyntax CreateCrefSyntax(NameMemberCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode parameterType) + { + CrefParameterListSyntax parameterList; + if (parameterType is TypeSyntax) + { + CrefParameterSyntax parameter = SyntaxFactory.CrefParameter((TypeSyntax)parameterType.ReplaceTokens(parameterType.DescendantTokens(), ReplaceBraceTokenForDocumentation)); + parameterList = SyntaxFactory.CrefParameterList(SyntaxFactory.SingletonSeparatedList(parameter)); + } + else + { + parameterList = SyntaxFactory.CrefParameterList(); + } + + return SyntaxFactory.NameMemberCref(SyntaxFactory.IdentifierName(identifierToken), parameterList); + } + + private static SyntaxToken ReplaceBraceTokenForDocumentation(SyntaxToken originalToken, SyntaxToken rewrittenToken) + { + switch (rewrittenToken.Kind()) + { + case SyntaxKind.LessThanToken: + case SyntaxKind.GreaterThanToken: + string newText = rewrittenToken.IsKind(SyntaxKind.LessThanToken) ? "{" : "}"; + return SyntaxFactory.Token(rewrittenToken.LeadingTrivia, rewrittenToken.Kind(), newText, rewrittenToken.ValueText, rewrittenToken.TrailingTrivia); + + default: + return rewrittenToken; + } + } + protected override ExpressionSyntax UnwrapCompoundAssignment( SyntaxNode compoundAssignment, ExpressionSyntax readExpression) { diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs index 441d6398354bd..e7b4cf13dff13 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs @@ -12,16 +12,20 @@ namespace Microsoft.CodeAnalysis.ReplacePropertyWithMethods { - internal abstract class AbstractReplacePropertyWithMethodsService + internal abstract class AbstractReplacePropertyWithMethodsService : IReplacePropertyWithMethodsService where TIdentifierNameSyntax : TExpressionSyntax where TExpressionSyntax : SyntaxNode + where TCrefSyntax : SyntaxNode where TStatementSyntax : SyntaxNode { public abstract SyntaxNode GetPropertyDeclaration(SyntaxToken token); public abstract SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclaration); public abstract Task> GetReplacementMembersAsync(Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, IFieldSymbol propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken); + protected abstract TCrefSyntax TryGetCrefSyntax(TIdentifierNameSyntax identifierName); + protected abstract TCrefSyntax CreateCrefSyntax(TCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode parameterType); + protected abstract TExpressionSyntax UnwrapCompoundAssignment(SyntaxNode compoundAssignment, TExpressionSyntax readExpression); protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldSymbol propertyBackingField) @@ -62,7 +66,7 @@ public async Task ReplaceReferenceAsync( private struct ReferenceReplacer { - private readonly AbstractReplacePropertyWithMethodsService _service; + private readonly AbstractReplacePropertyWithMethodsService _service; private readonly SemanticModel _semanticModel; private readonly ISyntaxFactsService _syntaxFacts; private readonly ISemanticFactsService _semanticFacts; @@ -75,10 +79,11 @@ private struct ReferenceReplacer private readonly TIdentifierNameSyntax _identifierName; private readonly TExpressionSyntax _expression; + private readonly TCrefSyntax _cref; private readonly CancellationToken _cancellationToken; public ReferenceReplacer( - AbstractReplacePropertyWithMethodsService service, + AbstractReplacePropertyWithMethodsService service, SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, ISemanticFactsService semanticFacts, @@ -102,6 +107,7 @@ public ReferenceReplacer( _identifierName = (TIdentifierNameSyntax)nameToken.Parent; _expression = _identifierName; + _cref = _service.TryGetCrefSyntax(_identifierName); if (_syntaxFacts.IsNameOfMemberAccessExpression(_expression)) { _expression = _expression.Parent as TExpressionSyntax; @@ -187,7 +193,13 @@ public ReferenceReplacer( public void Do() { - if (_semanticFacts.IsInOutContext(_semanticModel, _expression, _cancellationToken) || + if (_cref != null) + { + // We're in a documentation comment. Replace with a reference to the getter if one exists, + // otherwise to the setter. + ReplaceCref(); + } + else if (_semanticFacts.IsInOutContext(_semanticModel, _expression, _cancellationToken) || _semanticFacts.IsInRefContext(_semanticModel, _expression, _cancellationToken)) { // Code wasn't legal (you can't reference a property in an out/ref position in C#). @@ -247,6 +259,13 @@ public void Do() } } + private void ReplaceCref() + { + _editor.ReplaceNode( + _cref, + GetCrefReference(_cref)); + } + private void ReplaceRead(bool keepTrivia, string conflictMessage) { _editor.ReplaceNode( @@ -267,6 +286,24 @@ private void ReplaceWrite( new ReplaceParentArgs(this, getWriteValue, keepTrivia, conflictMessage)); } + private TCrefSyntax GetCrefReference(TCrefSyntax originalCref) + { + SyntaxToken newIdentifierToken; + SyntaxNode parameterType; + if (_property.GetMethod != null) + { + newIdentifierToken = Generator.Identifier(_desiredGetMethodName); + parameterType = null; + } + else + { + newIdentifierToken = Generator.Identifier(_desiredSetMethodName); + parameterType = Generator.TypeExpression(_property.Type); + } + + return _service.CreateCrefSyntax(originalCref, newIdentifierToken, parameterType); + } + private TExpressionSyntax GetReadExpression( bool keepTrivia, string conflictMessage) { diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs index 7263883b37e90..e4d73048534a6 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/ReplacePropertyWithMethodsCodeRefactoringProvider.cs @@ -245,7 +245,7 @@ private static async Task ReplaceReferencesAsync( var property = tuple.property; var referenceLocation = tuple.location; var location = referenceLocation.Location; - var nameToken = root.FindToken(location.SourceSpan.Start); + var nameToken = root.FindToken(location.SourceSpan.Start, findInsideTrivia: true); if (referenceLocation.IsImplicit) { diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 4f643a168c0ea..96628de7f95f3 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -110,7 +110,6 @@ - @@ -396,6 +395,8 @@ + + diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb new file mode 100644 index 0000000000000..d54c5d35f9a83 --- /dev/null +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb @@ -0,0 +1,44 @@ +' 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 Microsoft.CodeAnalysis.ReplacePropertyWithMethods +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty + Partial Friend Class VisualBasicReplacePropertyWithMethods + Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, CrefReferenceSyntax, StatementSyntax) + + Private Class ConvertValueToParamRewriter + Inherits VisualBasicSyntaxRewriter + + Public Shared ReadOnly instance As New ConvertValueToParamRewriter() + + Private Sub New() + End Sub + + Private Function IsValueName(node As XmlNodeSyntax) As Boolean + Dim name = TryCast(node, XmlNameSyntax) + Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value" + End Function + + Private Function ConvertToParam(name As XmlNodeSyntax) As SyntaxNode + Return name.ReplaceToken(DirectCast(name, XmlNameSyntax).LocalName, + SyntaxFactory.XmlNameToken("param", SyntaxKind.IdentifierToken)) + End Function + + Public Overrides Function VisitXmlElementStartTag(node As XmlElementStartTagSyntax) As SyntaxNode + If Not IsValueName(node.Name) Then + Return MyBase.VisitXmlElementStartTag(node) + End If + + Return node.ReplaceNode(node.Name, ConvertToParam(node.Name)) _ + .AddAttributes(SyntaxFactory.XmlNameAttribute("Value")) + End Function + + Public Overrides Function VisitXmlElementEndTag(node As XmlElementEndTagSyntax) As SyntaxNode + Return If(IsValueName(node.Name), + node.ReplaceNode(node.Name, ConvertToParam(node.Name)), + MyBase.VisitXmlElementEndTag(node)) + End Function + End Class + End Class +End Namespace diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/ConvertValueToReturnsRewriter.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb similarity index 96% rename from src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/ConvertValueToReturnsRewriter.vb rename to src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb index ea4f860d722e0..3972ac44e05d0 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/ConvertValueToReturnsRewriter.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb @@ -5,7 +5,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty Partial Friend Class VisualBasicReplacePropertyWithMethods - Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax) + Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, CrefReferenceSyntax, StatementSyntax) Private Class ConvertValueToReturnsRewriter Inherits VisualBasicSyntaxRewriter diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb index e11298b5d285b..3dcd602d189e0 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb @@ -11,7 +11,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty Partial Friend Class VisualBasicReplacePropertyWithMethods - Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax) + Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, CrefReferenceSyntax, StatementSyntax) Public Overrides Function GetPropertyDeclaration(token As SyntaxToken) As SyntaxNode Dim containingProperty = token.Parent.FirstAncestorOrSelf(Of PropertyStatementSyntax) @@ -84,7 +84,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP result.Add(GetGetMethod( generator, propertyStatement, propertyBackingField, getMethod, desiredGetMethodName, - copyLeadingTrivia:=True, cancellationToken:=cancellationToken)) End If @@ -93,7 +92,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP result.Add(GetSetMethod( generator, propertyStatement, propertyBackingField, setMethod, desiredSetMethodName, - copyLeadingTrivia:=getMethod Is Nothing, cancellationToken:=cancellationToken)) End If @@ -106,7 +104,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP propertyBackingField As IFieldSymbol, getMethod As IMethodSymbol, desiredGetMethodName As String, - copyLeadingTrivia As Boolean, cancellationToken As CancellationToken) As SyntaxNode Dim statements = New List(Of SyntaxNode)() @@ -123,7 +120,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP End If Dim methodDeclaration = generator.MethodDeclaration(getMethod, desiredGetMethodName, statements) - methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia) + methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToReturnsRewriter.instance) Return methodDeclaration End Function @@ -137,7 +134,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP propertyBackingField As IFieldSymbol, setMethod As IMethodSymbol, desiredSetMethodName As String, - copyLeadingTrivia As Boolean, cancellationToken As CancellationToken) As SyntaxNode Dim statements = New List(Of SyntaxNode)() @@ -155,24 +151,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP End If Dim methodDeclaration = generator.MethodDeclaration(setMethod, desiredSetMethodName, statements) - methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia) + methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToParamRewriter.instance) Return methodDeclaration End Function Private Function CopyLeadingTriviaOver(propertyStatement As PropertyStatementSyntax, methodDeclaration As SyntaxNode, - copyLeadingTrivia As Boolean) As SyntaxNode - If copyLeadingTrivia Then - Return methodDeclaration.WithLeadingTrivia( - propertyStatement.GetLeadingTrivia().Select(AddressOf ConvertTrivia)) - End If - - Return methodDeclaration + documentationCommentRewriter As VisualBasicSyntaxRewriter) As SyntaxNode + Return methodDeclaration.WithLeadingTrivia( + propertyStatement.GetLeadingTrivia().Select(Function(trivia) ConvertTrivia(trivia, documentationCommentRewriter))) End Function - Private Function ConvertTrivia(trivia As SyntaxTrivia) As SyntaxTrivia + Private Function ConvertTrivia(trivia As SyntaxTrivia, documentationCommentRewriter As VisualBasicSyntaxRewriter) As SyntaxTrivia If trivia.Kind() = SyntaxKind.DocumentationCommentTrivia Then - Dim converted = ConvertValueToReturnsRewriter.instance.Visit(trivia.GetStructure()) + Dim converted = documentationCommentRewriter.Visit(trivia.GetStructure()) Return SyntaxFactory.Trivia(DirectCast(converted, StructuredTriviaSyntax)) End If @@ -188,6 +180,38 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP propertyDeclaration) End Function + Protected Overrides Function TryGetCrefSyntax(identifierName As IdentifierNameSyntax) As CrefReferenceSyntax + Dim simpleNameCref = TryCast(identifierName.Parent, CrefReferenceSyntax) + If simpleNameCref IsNot Nothing Then + Return simpleNameCref + End If + + Dim qualifiedName = TryCast(identifierName.Parent, QualifiedNameSyntax) + If qualifiedName Is Nothing Then + Return Nothing + End If + + Return TryCast(qualifiedName.Parent, CrefReferenceSyntax) + End Function + + Protected Overrides Function CreateCrefSyntax(originalCref As CrefReferenceSyntax, identifierToken As SyntaxToken, parameterType As SyntaxNode) As CrefReferenceSyntax + Dim signature As CrefSignatureSyntax + Dim parameterSyntax = TryCast(parameterType, TypeSyntax) + If parameterSyntax IsNot Nothing Then + signature = SyntaxFactory.CrefSignature(SyntaxFactory.CrefSignaturePart(Nothing, parameterSyntax)) + Else + signature = SyntaxFactory.CrefSignature() + End If + + Dim typeReference As TypeSyntax = SyntaxFactory.IdentifierName(identifierToken) + Dim qualifiedType = TryCast(originalCref.Name, QualifiedNameSyntax) + If qualifiedType IsNot Nothing Then + typeReference = qualifiedType.ReplaceNode(qualifiedType.GetLastDottedName(), typeReference) + End If + + Return SyntaxFactory.CrefReference(typeReference, signature, Nothing) + End Function + Protected Overrides Function UnwrapCompoundAssignment(compoundAssignment As SyntaxNode, readExpression As ExpressionSyntax) As ExpressionSyntax Throw New InvalidOperationException("Compound assignments don't exist in VB") End Function From 82e79be79c2972dd9fb3379d2d3e8808e8533ad4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 28 Mar 2017 16:05:19 -0500 Subject: [PATCH 012/214] Ignore test broken due to VB formatting bug See #18261 --- .../ReplacePropertyWithMethodsTests.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb index 1665a8a472717..43dc0b88e8055 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.vb @@ -556,7 +556,7 @@ End Structure", ignoreTrivia:=False) End Function - + Public Async Function TestDocumentationComment6() As Task Await TestInRegularAndScriptAsync( "Interface ISomeInterface(Of T) From a206a024295605e5968683a82fc9d74fcf07df75 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 28 Mar 2017 16:49:49 -0500 Subject: [PATCH 013/214] Code cleanup based on code review feedback --- ...hodsService.ConvertValueToParamRewriter.cs | 4 --- ...dsService.ConvertValueToReturnsRewriter.cs | 4 --- ...CSharpReplacePropertyWithMethodsService.cs | 36 +++++++++---------- ...stractReplacePropertyWithMethodsService.cs | 9 +---- ...WithMethods.ConvertValueToParamRewriter.vb | 5 --- ...thMethods.ConvertValueToReturnsRewriter.vb | 5 --- .../VisualBasicReplacePropertyWithMethods.vb | 8 +++++ 7 files changed, 25 insertions(+), 46 deletions(-) diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs index 7f1d5ffa06e37..e3e529ba9672f 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs @@ -17,10 +17,6 @@ private ConvertValueToParamRewriter() private XmlNameSyntax ConvertToParam(XmlNameSyntax name) => name.ReplaceToken(name.LocalName, SyntaxFactory.Identifier("param")); - private static bool IsValueName(XmlNameSyntax name) - => name.Prefix == null && - name.LocalName.ValueText == "value"; - public override SyntaxNode VisitXmlElementStartTag(XmlElementStartTagSyntax node) { if (!IsValueName(node.Name)) diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs index c42bee0bfce7f..fac0141a9293c 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs @@ -17,10 +17,6 @@ private ConvertValueToReturnsRewriter() private XmlNameSyntax ConvertToReturns(XmlNameSyntax name) => name.ReplaceToken(name.LocalName, SyntaxFactory.Identifier("returns")); - private static bool IsValueName(XmlNameSyntax name) - => name.Prefix == null && - name.LocalName.ValueText == "value"; - public override SyntaxNode VisitXmlElementStartTag(XmlElementStartTagSyntax node) => IsValueName(node.Name) ? node.ReplaceNode(node.Name, ConvertToReturns(node.Name)) diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs index e99e7ee6b3500..021302a4fd21a 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs @@ -128,6 +128,8 @@ private static SyntaxNode GetSetMethod( generator, propertyDeclaration, propertyBackingField, setMethod, desiredSetMethodName, cancellationToken); + // The analyzer doesn't report diagnostics when the trivia contains preprocessor directives, so it's safe + // to copy the complete leading trivia to both generated methods. methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, ConvertValueToParamRewriter.Instance); return UseExpressionOrBlockBodyIfDesired( @@ -288,6 +290,13 @@ private static MethodDeclarationSyntax GetGetMethodWorker( return methodDeclaration; } + /// + /// Used by the documentation comment rewriters to identify top-level <value> nodes. + /// + private static bool IsValueName(XmlNameSyntax name) + => name.Prefix == null && + name.LocalName.ValueText == "value"; + public override SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclaration) { // For C# we'll have the property declaration that we want to replace. @@ -295,16 +304,14 @@ public override SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclarati } protected override NameMemberCrefSyntax TryGetCrefSyntax(IdentifierNameSyntax identifierName) - { - return identifierName.Parent as NameMemberCrefSyntax; - } + => identifierName.Parent as NameMemberCrefSyntax; protected override NameMemberCrefSyntax CreateCrefSyntax(NameMemberCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode parameterType) { CrefParameterListSyntax parameterList; - if (parameterType is TypeSyntax) + if (parameterType is TypeSyntax typeSyntax) { - CrefParameterSyntax parameter = SyntaxFactory.CrefParameter((TypeSyntax)parameterType.ReplaceTokens(parameterType.DescendantTokens(), ReplaceBraceTokenForDocumentation)); + CrefParameterSyntax parameter = SyntaxFactory.CrefParameter(typeSyntax); parameterList = SyntaxFactory.CrefParameterList(SyntaxFactory.SingletonSeparatedList(parameter)); } else @@ -312,21 +319,10 @@ protected override NameMemberCrefSyntax CreateCrefSyntax(NameMemberCrefSyntax or parameterList = SyntaxFactory.CrefParameterList(); } - return SyntaxFactory.NameMemberCref(SyntaxFactory.IdentifierName(identifierToken), parameterList); - } - - private static SyntaxToken ReplaceBraceTokenForDocumentation(SyntaxToken originalToken, SyntaxToken rewrittenToken) - { - switch (rewrittenToken.Kind()) - { - case SyntaxKind.LessThanToken: - case SyntaxKind.GreaterThanToken: - string newText = rewrittenToken.IsKind(SyntaxKind.LessThanToken) ? "{" : "}"; - return SyntaxFactory.Token(rewrittenToken.LeadingTrivia, rewrittenToken.Kind(), newText, rewrittenToken.ValueText, rewrittenToken.TrailingTrivia); - - default: - return rewrittenToken; - } + // XmlCrefAttribute replaces with {T}, which is required for C# documentation comments + var crefAttribute = SyntaxFactory.XmlCrefAttribute( + SyntaxFactory.NameMemberCref(SyntaxFactory.IdentifierName(identifierToken), parameterList)); + return (NameMemberCrefSyntax)crefAttribute.Cref; } protected override ExpressionSyntax UnwrapCompoundAssignment( diff --git a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs index e7b4cf13dff13..b60035b93911d 100644 --- a/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs +++ b/src/Features/Core/Portable/ReplacePropertyWithMethods/AbstractReplacePropertyWithMethodsService.cs @@ -197,7 +197,7 @@ public void Do() { // We're in a documentation comment. Replace with a reference to the getter if one exists, // otherwise to the setter. - ReplaceCref(); + _editor.ReplaceNode(_cref, GetCrefReference(_cref)); } else if (_semanticFacts.IsInOutContext(_semanticModel, _expression, _cancellationToken) || _semanticFacts.IsInRefContext(_semanticModel, _expression, _cancellationToken)) @@ -259,13 +259,6 @@ public void Do() } } - private void ReplaceCref() - { - _editor.ReplaceNode( - _cref, - GetCrefReference(_cref)); - } - private void ReplaceRead(bool keepTrivia, string conflictMessage) { _editor.ReplaceNode( diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb index d54c5d35f9a83..5ab92da12644f 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb @@ -15,11 +15,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP Private Sub New() End Sub - Private Function IsValueName(node As XmlNodeSyntax) As Boolean - Dim name = TryCast(node, XmlNameSyntax) - Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value" - End Function - Private Function ConvertToParam(name As XmlNodeSyntax) As SyntaxNode Return name.ReplaceToken(DirectCast(name, XmlNameSyntax).LocalName, SyntaxFactory.XmlNameToken("param", SyntaxKind.IdentifierToken)) diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb index 3972ac44e05d0..8b6ac614c4908 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb @@ -15,11 +15,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP Private Sub New() End Sub - Private Function IsValueName(node As XmlNodeSyntax) As Boolean - Dim name = TryCast(node, XmlNameSyntax) - Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value" - End Function - Private Function ConvertToReturns(name As XmlNodeSyntax) As SyntaxNode Return name.ReplaceToken(DirectCast(name, XmlNameSyntax).LocalName, SyntaxFactory.XmlNameToken("returns", SyntaxKind.IdentifierToken)) diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb index 3dcd602d189e0..324f9eb676898 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb @@ -171,6 +171,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP Return trivia End Function + ''' + ''' Used by the documentation comment rewriters to identify top-level <value> nodes. + ''' + Private Shared Function IsValueName(node As XmlNodeSyntax) As Boolean + Dim name = TryCast(node, XmlNameSyntax) + Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value" + End Function + Public Overrides Function GetPropertyNodeToReplace(propertyDeclaration As SyntaxNode) As SyntaxNode ' In VB we'll have the property statement. If that is parented by a ' property block, we'll want to replace that instead. Otherwise we From c2731508330fddbbdbc8873df0fbf7a138c1f741 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sun, 23 Apr 2017 10:25:28 -0500 Subject: [PATCH 014/214] Code cleanup based on code review feedback --- .../CSharpReplacePropertyWithMethodsService.cs | 2 +- .../VisualBasicReplacePropertyWithMethods.vb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs index 021302a4fd21a..132d111a1c0c7 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs @@ -311,7 +311,7 @@ protected override NameMemberCrefSyntax CreateCrefSyntax(NameMemberCrefSyntax or CrefParameterListSyntax parameterList; if (parameterType is TypeSyntax typeSyntax) { - CrefParameterSyntax parameter = SyntaxFactory.CrefParameter(typeSyntax); + var parameter = SyntaxFactory.CrefParameter(typeSyntax); parameterList = SyntaxFactory.CrefParameterList(SyntaxFactory.SingletonSeparatedList(parameter)); } else diff --git a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb index 324f9eb676898..cf53f99d3bb13 100644 --- a/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb +++ b/src/Features/VisualBasic/Portable/ReplacePropertyWithMethods/VisualBasicReplacePropertyWithMethods.vb @@ -206,7 +206,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP Dim signature As CrefSignatureSyntax Dim parameterSyntax = TryCast(parameterType, TypeSyntax) If parameterSyntax IsNot Nothing Then - signature = SyntaxFactory.CrefSignature(SyntaxFactory.CrefSignaturePart(Nothing, parameterSyntax)) + signature = SyntaxFactory.CrefSignature(SyntaxFactory.CrefSignaturePart(modifier:=Nothing, type:=parameterSyntax)) Else signature = SyntaxFactory.CrefSignature() End If @@ -217,7 +217,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP typeReference = qualifiedType.ReplaceNode(qualifiedType.GetLastDottedName(), typeReference) End If - Return SyntaxFactory.CrefReference(typeReference, signature, Nothing) + Return SyntaxFactory.CrefReference(typeReference, signature, asClause:=Nothing) End Function Protected Overrides Function UnwrapCompoundAssignment(compoundAssignment As SyntaxNode, readExpression As ExpressionSyntax) As ExpressionSyntax From 81b8036b39a43f6889e9974bf21e439b8063c71f Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 25 Apr 2017 04:57:57 -0700 Subject: [PATCH 015/214] Ensure that we get a well formed child IOperation tree even for bound nodes for whom IOperation support is not yet implemented This ensures that the analyzer driver/operation walker is able to find all the descendant IOperation nodes within these not yet implemented features. We should remove the IOperationWithChildren interface once we have designed all the IOperation APIs. TODO: Implement the VB part. Fixes #8884 --- .../CSharp/Portable/BoundTree/Expression.cs | 141 ++++- .../CSharp/Portable/BoundTree/Statement.cs | 68 +- .../AsyncRewriter/AwaitExpressionSpiller.cs | 2 + .../CSharpCompilerSemanticTest.csproj | 1 + .../IOperationTests_IIfStatement.cs | 36 +- ...tionTests_IParameterReferenceExpression.cs | 580 ++++++++++++++++++ .../Core/Portable/CodeAnalysis.csproj | 1 + .../Operations/IOperationWithChildren.cs | 19 + .../Portable/Operations/OperationWalker.cs | 8 + .../Utilities/CSharp/SemanticModelTestBase.cs | 4 +- .../Compilation/OperationTreeVerifier.cs | 6 + 11 files changed, 816 insertions(+), 50 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs create mode 100644 src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 0ffce8044a46d..4fe0c3c5a38cc 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -1,25 +1,27 @@ // 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.Immutable; using System.Diagnostics; using System.Runtime.CompilerServices; +using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Semantics; using Roslyn.Utilities; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Microsoft.CodeAnalysis.Collections; namespace Microsoft.CodeAnalysis.CSharp { - internal partial class BoundExpression : IOperation + internal partial class BoundExpression { - ITypeSymbol IOperation.Type => this.Type; + protected override OperationKind OperationKind => this.ExpressionKind; + + protected override ITypeSymbol OperationType => this.Type; + + protected abstract OperationKind ExpressionKind { get; } - OperationKind IOperation.Kind => this.ExpressionKind; + public override abstract void Accept(OperationVisitor visitor); - bool IOperation.IsInvalid => this.HasErrors; + public override abstract TResult Accept(OperationVisitor visitor, TArgument argument); - Optional IOperation.ConstantValue + protected override Optional OperationConstantValue { get { @@ -27,13 +29,6 @@ Optional IOperation.ConstantValue return value != null ? new Optional(value.Value) : default(Optional); } } - SyntaxNode IOperation.Syntax => this.Syntax; - - protected abstract OperationKind ExpressionKind { get; } - - public abstract void Accept(OperationVisitor visitor); - - public abstract TResult Accept(OperationVisitor visitor, TArgument argument); } internal sealed partial class BoundDeconstructValuePlaceholder : BoundValuePlaceholderBase, IPlaceholderExpression @@ -498,6 +493,8 @@ internal partial class BoundTupleExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -525,11 +522,11 @@ ImmutableArray IObjectCreationExpression.MemberInitializers return (ImmutableArray)s_memberInitializersMappings.GetValue(this, objectCreationExpression => { - var objectInitializerExpression = this.InitializerExpressionOpt as BoundObjectInitializerExpression; - if (objectInitializerExpression != null) + var initializers = GetChildInitializers(objectCreationExpression.InitializerExpressionOpt); + if (!initializers.IsEmpty) { - var builder = ArrayBuilder.GetInstance(objectInitializerExpression.Initializers.Length); - foreach (var memberAssignment in objectInitializerExpression.Initializers) + var builder = ArrayBuilder.GetInstance(initializers.Length); + foreach (var memberAssignment in initializers) { var assignment = memberAssignment as BoundAssignmentOperator; var leftSymbol = (assignment?.Left as BoundObjectInitializerMember)?.MemberSymbol; @@ -551,11 +548,29 @@ ImmutableArray IObjectCreationExpression.MemberInitializers } return builder.ToImmutableAndFree(); } + return ImmutableArray.Empty; }); } } + internal static ImmutableArray GetChildInitializers(BoundExpression objectOrCollectionInitializer) + { + var objectInitializerExpression = objectOrCollectionInitializer as BoundObjectInitializerExpression; + if (objectInitializerExpression != null) + { + return objectInitializerExpression.Initializers; + } + + var collectionInitializerExpresion = objectOrCollectionInitializer as BoundCollectionInitializerExpression; + if (collectionInitializerExpresion != null) + { + return collectionInitializerExpresion.Initializers; + } + + return ImmutableArray.Empty; + } + protected override OperationKind ExpressionKind => OperationKind.ObjectCreationExpression; public override void Accept(OperationVisitor visitor) @@ -1001,6 +1016,8 @@ internal sealed partial class BoundDeconstructionAssignmentOperator : BoundExpre // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Left, this.Right); + public override void Accept(OperationVisitor visitor) { // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) @@ -1341,21 +1358,11 @@ internal partial class BoundEqualsValue : ISymbolInitializer { IOperation ISymbolInitializer.Value => this.Value; - SyntaxNode IOperation.Syntax => this.Syntax; - bool IOperation.IsInvalid => ((IOperation)this.Value).IsInvalid; - OperationKind IOperation.Kind => this.OperationKind; - - protected abstract OperationKind OperationKind { get; } - - ITypeSymbol IOperation.Type => null; - - Optional IOperation.ConstantValue => default(Optional); + public override abstract void Accept(OperationVisitor visitor); - public abstract void Accept(OperationVisitor visitor); - - public abstract TResult Accept(OperationVisitor visitor, TArgument argument); + public override abstract TResult Accept(OperationVisitor visitor, TArgument argument); } internal partial class BoundFieldEqualsValue : IFieldInitializer @@ -1413,6 +1420,8 @@ internal partial class BoundDynamicIndexerAccess { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.Concat(this.ReceiverOpt).As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1428,6 +1437,8 @@ internal partial class BoundUserDefinedConditionalLogicalOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Left, this.Right); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1443,6 +1454,8 @@ internal partial class BoundAnonymousObjectCreationExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1473,6 +1486,8 @@ internal partial class BoundAttribute { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.ConstructorArguments.Concat(this.NamedArguments).As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1488,6 +1503,8 @@ internal partial class BoundRangeVariable { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Value); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1533,6 +1550,8 @@ internal partial class BoundQueryClause { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Value); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1544,10 +1563,32 @@ public override TResult Accept(OperationVisitor Children { get; } + } + + internal partial class BoundDeclarationPattern + { + protected override ImmutableArray Children => ImmutableArray.Create(this.VariableAccess, this.DeclaredType); + } + + internal partial class BoundConstantPattern + { + protected override ImmutableArray Children => ImmutableArray.Create(this.Value); + } + + internal partial class BoundWildcardPattern + { + protected override ImmutableArray Children => ImmutableArray.Empty; + } + internal partial class BoundArgListOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1578,6 +1619,8 @@ internal partial class BoundCollectionElementInitializer { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1593,6 +1636,8 @@ internal partial class BoundNameOfOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Argument); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1743,6 +1788,8 @@ internal partial class BoundPointerElementAccess { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Expression, this.Index); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1758,6 +1805,8 @@ internal partial class BoundRefTypeOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Operand); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1773,6 +1822,8 @@ internal partial class BoundDynamicMemberAccess { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Receiver); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1788,6 +1839,8 @@ internal partial class BoundMakeRefOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Operand); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1803,6 +1856,8 @@ internal partial class BoundRefValueOperator { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Operand); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1818,6 +1873,8 @@ internal partial class BoundDynamicInvocation { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As().Concat(this.Expression); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1833,6 +1890,8 @@ internal partial class BoundArrayLength { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1953,6 +2012,8 @@ internal partial class BoundCollectionInitializerExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Initializers.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2013,6 +2074,8 @@ internal partial class BoundDynamicCollectionElementInitializer { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2043,6 +2106,8 @@ internal partial class BoundFixedLocalCollectionInitializer { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2058,6 +2123,8 @@ internal partial class BoundStackAllocArrayCreation { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Count); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2073,6 +2140,8 @@ internal partial class BoundDynamicObjectCreationExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Arguments.Concat(BoundObjectCreationExpression.GetChildInitializers(this.InitializerExpressionOpt)).As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2103,6 +2172,8 @@ internal partial class BoundInterpolatedString { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Parts.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2118,6 +2189,8 @@ internal partial class BoundNoPiaObjectCreationExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => BoundObjectCreationExpression.GetChildInitializers(this.InitializerExpressionOpt).As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2133,6 +2206,8 @@ internal partial class BoundObjectInitializerExpression { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => this.Initializers.As(); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2148,6 +2223,8 @@ internal partial class BoundStringInsert { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Value, this.Alignment, this.Format); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2961,6 +3038,8 @@ public override TResult Accept(OperationVisitor OperationKind.None; + + protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); } internal partial class BoundDeclarationPattern diff --git a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs index 54905fbe5080c..57ea3ecc34bc2 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs @@ -9,23 +9,51 @@ namespace Microsoft.CodeAnalysis.CSharp { - internal partial class BoundStatement : IOperation + internal partial class BoundNode : IOperation, IOperationWithChildren { - OperationKind IOperation.Kind => this.StatementKind; + OperationKind IOperation.Kind => this.OperationKind; bool IOperation.IsInvalid => this.HasErrors; SyntaxNode IOperation.Syntax => this.Syntax; - ITypeSymbol IOperation.Type => null; + ITypeSymbol IOperation.Type => OperationType; - Optional IOperation.ConstantValue => default(Optional); + Optional IOperation.ConstantValue => this.OperationConstantValue; + + ImmutableArray IOperationWithChildren.Children => this.Children; + + protected virtual OperationKind OperationKind => OperationKind.None; + + protected virtual ITypeSymbol OperationType => null; + + protected virtual Optional OperationConstantValue => default(Optional); + + /// + /// Override this property to return the child operations if the IOperation API corresponding to this bound node is not yet designed or implemented. + /// + protected virtual ImmutableArray Children => ImmutableArray.Empty; + + public virtual void Accept(OperationVisitor visitor) + { + visitor.VisitNoneOperation(this); + } + + public virtual TResult Accept(OperationVisitor visitor, TArgument argument) + { + return visitor.VisitNoneOperation(this, argument); + } + } + + internal partial class BoundStatement + { + protected override OperationKind OperationKind => this.StatementKind; protected abstract OperationKind StatementKind { get; } - public abstract void Accept(OperationVisitor visitor); + public override abstract void Accept(OperationVisitor visitor); - public abstract TResult Accept(OperationVisitor visitor, TArgument argument); + public override abstract TResult Accept(OperationVisitor visitor, TArgument argument); } internal partial class BoundBlock : IBlockStatement @@ -767,6 +795,8 @@ internal partial class BoundConditionalGoto { protected override OperationKind StatementKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Condition); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -843,6 +873,22 @@ partial class BoundPatternSwitchStatement // TODO: this may need its own OperationKind. protected override OperationKind StatementKind => OperationKind.None; + protected override ImmutableArray Children + { + get + { + var builder = ImmutableArray.CreateBuilder(this.SwitchSections.Length + 2); + builder.Add(this.Expression); + foreach (var section in this.SwitchSections) + { + builder.Add(section); + } + builder.Add(this.DefaultLabel); + + return builder.ToImmutable(); + } + } + public override void Accept(OperationVisitor visitor) { // TODO: implement IOperation for pattern-matching constructs (https://github.com/dotnet/roslyn/issues/8699) @@ -855,4 +901,14 @@ public override TResult Accept(OperationVisitor Children => this.SwitchLabels.As().Concat(this.Statements).ToImmutableArray(); + } + + partial class BoundPatternSwitchLabel + { + protected override ImmutableArray Children => ImmutableArray.Create(this.Pattern, this.Guard); + } } diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs index 1306f545dbe01..692d6ac69428b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs @@ -58,6 +58,8 @@ public bool HasLocals protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Value); + public override void Accept(OperationVisitor visitor) { throw ExceptionUtilities.Unreachable; diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index c6e668ed7af85..e391ca969f34e 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs index 92accf3ac9960..862397adaf8b0 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IIfStatement.cs @@ -331,28 +331,40 @@ private void M() { var s = """"; /**/if (int.TryParse(s, out var i)) - System.Console.WriteLine($""i ={ i}, s ={ s}""); + System.Console.WriteLine($""i ={i}, s ={s}""); else - System.Console.WriteLine($""i ={ i}, s ={ s}"");/**/ + System.Console.WriteLine($""i ={ i}, s ={s}"");/**/ } } "; string expectedOperationTree = @" -IIfStatement (OperationKind.IfStatement) (Syntax: 'if (int.Try ... s ={ s}"");') +IIfStatement (OperationKind.IfStatement) (Syntax: 'if (int.Try ... , s ={s}"");') Condition: IInvocationExpression (static System.Boolean System.Int32.TryParse(System.String s, out System.Int32 result)) (OperationKind.InvocationExpression, Type: System.Boolean) (Syntax: 'int.TryPars ... out var i)') Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: s) (OperationKind.Argument) (Syntax: 's') ILocalReferenceExpression: s (OperationKind.LocalReferenceExpression, Type: System.String) (Syntax: 's') IArgument (ArgumentKind.Explicit, Matching Parameter: result) (OperationKind.Argument) (Syntax: 'var i') ILocalReferenceExpression: i (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var i') - IfTrue: IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'System.Cons ... s ={ s}"");') - IInvocationExpression (static void System.Console.WriteLine(System.String value)) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'System.Cons ... , s ={ s}"")') - Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument) (Syntax: '$""i ={ i}, s ={ s}""') - IOperation: (OperationKind.None) (Syntax: '$""i ={ i}, s ={ s}""') - IfFalse: IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'System.Cons ... s ={ s}"");') - IInvocationExpression (static void System.Console.WriteLine(System.String value)) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'System.Cons ... , s ={ s}"")') - Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument) (Syntax: '$""i ={ i}, s ={ s}""') - IOperation: (OperationKind.None) (Syntax: '$""i ={ i}, s ={ s}""') + IfTrue: IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'System.Cons ... , s ={s}"");') + IInvocationExpression (static void System.Console.WriteLine(System.String value)) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'System.Cons ... }, s ={s}"")') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument) (Syntax: '$""i ={i}, s ={s}""') + IOperation: (OperationKind.None) (Syntax: '$""i ={i}, s ={s}""') + Children(4): ILiteralExpression (Text: i =) (OperationKind.LiteralExpression, Type: System.String, Constant: ""i ="") (Syntax: 'i =') + IOperation: (OperationKind.None) (Syntax: '{i}') + Children(1): ILocalReferenceExpression: i (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'i') + ILiteralExpression (Text: , s =) (OperationKind.LiteralExpression, Type: System.String, Constant: "", s ="") (Syntax: ', s =') + IOperation: (OperationKind.None) (Syntax: '{s}') + Children(1): ILocalReferenceExpression: s (OperationKind.LocalReferenceExpression, Type: System.String) (Syntax: 's') + IfFalse: IExpressionStatement (OperationKind.ExpressionStatement) (Syntax: 'System.Cons ... , s ={s}"");') + IInvocationExpression (static void System.Console.WriteLine(System.String value)) (OperationKind.InvocationExpression, Type: System.Void) (Syntax: 'System.Cons ... }, s ={s}"")') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument) (Syntax: '$""i ={ i}, s ={s}""') + IOperation: (OperationKind.None) (Syntax: '$""i ={ i}, s ={s}""') + Children(4): ILiteralExpression (Text: i =) (OperationKind.LiteralExpression, Type: System.String, Constant: ""i ="") (Syntax: 'i =') + IOperation: (OperationKind.None) (Syntax: '{ i}') + Children(1): ILocalReferenceExpression: i (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'i') + ILiteralExpression (Text: , s =) (OperationKind.LiteralExpression, Type: System.String, Constant: "", s ="") (Syntax: ', s =') + IOperation: (OperationKind.None) (Syntax: '{s}') + Children(1): ILocalReferenceExpression: s (OperationKind.LocalReferenceExpression, Type: System.String) (Syntax: 's') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -796,6 +808,8 @@ public static int F(dynamic d, Type t, T x) where T : struct IBinaryOperatorExpression (BinaryOperationKind.DynamicAnd) (OperationKind.BinaryOperatorExpression, Type: dynamic) (Syntax: 'd.GetType() ... ).Equals(x)') Left: IBinaryOperatorExpression (BinaryOperationKind.Invalid) (OperationKind.BinaryOperatorExpression, Type: dynamic) (Syntax: 'd.GetType() == t') Left: IOperation: (OperationKind.None) (Syntax: 'd.GetType()') + Children(1): IOperation: (OperationKind.None) (Syntax: 'd.GetType') + Children(1): IParameterReferenceExpression: d (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'd') Right: IParameterReferenceExpression: t (OperationKind.ParameterReferenceExpression, Type: System.Type) (Syntax: 't') Right: IInvocationExpression (virtual System.Boolean System.ValueType.Equals(System.Object obj)) (OperationKind.InvocationExpression, Type: System.Boolean) (Syntax: '((T)d).Equals(x)') Instance Receiver: IConversionExpression (ConversionKind.CSharp, Explicit) (OperationKind.ConversionExpression, Type: T) (Syntax: '(T)d') diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs new file mode 100644 index 0000000000000..fc9f7c2d3a95a --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -0,0 +1,580 @@ +// 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.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public partial class IOperationTests : SemanticModelTestBase + { + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_TupleExpression() + { + string source = @" +class Class1 +{ + public void M(int x, int y) + { + var tuple = /**/(x, x + y)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '(x, x + y)') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IBinaryOperatorExpression (BinaryOperationKind.IntegerAdd) (OperationKind.BinaryOperatorExpression, Type: System.Int32) (Syntax: 'x + y') + Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_TupleDeconstruction() + { + string source = @" +class Point +{ + public int X { get; } + public int Y { get; } + + public Point(int x, int y) + { + X = x; + Y = y; + } + + public void Deconstruct(out int x, out int y) + { + x = X; + y = Y; + } +} + +class Class1 +{ + public void M(Point point) + { + /**/var (x, y) = point/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'var (x, y) = point') + Children(2): IOperation: (OperationKind.None) (Syntax: 'var (x, y)') + Children(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'x') + ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') + IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32, System.Int32)) (Syntax: 'point') + IParameterReferenceExpression: point (OperationKind.ParameterReferenceExpression, Type: Point) (Syntax: 'point') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_AnonymousObjectCreation() + { + string source = @" +class Class1 +{ + public void M(int x, string y) + { + var v = /**/new { Amount = x, Message = ""Hello"" + y }/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'new { Amoun ... ello"" + y }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IBinaryOperatorExpression (BinaryOperationKind.StringConcatenate) (OperationKind.BinaryOperatorExpression, Type: System.String) (Syntax: '""Hello"" + y') + Left: ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: ""Hello"") (Syntax: '""Hello""') + Right: IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.String) (Syntax: 'y') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_QueryExpression() + { + string source = @" +using System.Linq; +using System.Collections.Generic; + +struct Customer +{ + public string Name { get; set; } + public string Address { get; set; } +} + +class Class1 +{ + public void M(List customers) + { + var result = /**/from cust in customers + select cust.Name/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'from cust i ... t cust.Name') + Children(1): IOperation: (OperationKind.None) (Syntax: 'select cust.Name') + Children(1): IInvocationExpression (static System.Collections.Generic.IEnumerable System.Linq.Enumerable.Select(this System.Collections.Generic.IEnumerable source, System.Func selector)) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: 'select cust.Name') + Arguments(2): IArgument (ArgumentKind.Explicit, Matching Parameter: source) (OperationKind.Argument) (Syntax: 'from cust in customers') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable) (Syntax: 'from cust in customers') + IOperation: (OperationKind.None) (Syntax: 'from cust in customers') + Children(1): IParameterReferenceExpression: customers (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.List) (Syntax: 'customers') + IArgument (ArgumentKind.Explicit, Matching Parameter: selector) (OperationKind.Argument) (Syntax: 'cust.Name') + IConversionExpression (ConversionKind.CSharp, Implicit) (OperationKind.ConversionExpression, Type: System.Func) (Syntax: 'cust.Name') + ILambdaExpression (Signature: lambda expression) (OperationKind.LambdaExpression, Type: System.Func) (Syntax: 'cust.Name') + IBlockStatement (1 statements) (OperationKind.BlockStatement) (Syntax: 'cust.Name') + IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'cust.Name') + IPropertyReferenceExpression: System.String Customer.Name { get; set; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name') + Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust') + Children(1): IParameterReferenceExpression: cust (OperationKind.ParameterReferenceExpression, Type: Customer) (Syntax: 'cust') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_ObjectAndCollectionInitializer() + { + string source = @" +using System.Collections.Generic; + +internal class Class +{ + public int X { get; set; } + public List Y { get; set; } + public Dictionary Z { get; set; } + public Class C { get; set; } + + public void M(int x, int y, int z) + { + var c = /**/new Class() { X = x, Y = { x, y, 3 }, Z = { { x, y } }, C = { X = z } }/**/; + } +} +"; + string expectedOperationTree = @" +IObjectCreationExpression (Constructor: Class..ctor()) (OperationKind.ObjectCreationExpression, Type: Class) (Syntax: 'new Class() ... { X = z } }') + Member Initializers(4): IPropertyInitializer (Property: System.Int32 Class.X { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'X = x') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IPropertyInitializer (Property: System.Collections.Generic.List Class.Y { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Y = { x, y, 3 }') + IOperation: (OperationKind.None) (Syntax: '{ x, y, 3 }') + Children(3): IOperation: (OperationKind.None) (Syntax: 'x') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'y') + Children(1): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IOperation: (OperationKind.None) (Syntax: '3') + Children(1): ILiteralExpression (Text: 3) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 3) (Syntax: '3') + IPropertyInitializer (Property: System.Collections.Generic.Dictionary Class.Z { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'Z = { { x, y } }') + IOperation: (OperationKind.None) (Syntax: '{ { x, y } }') + Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IPropertyInitializer (Property: Class Class.C { get; set; }) (OperationKind.PropertyInitializerInCreation) (Syntax: 'C = { X = z }') + IOperation: (OperationKind.None) (Syntax: '{ X = z }') + Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: System.Int32) (Syntax: 'X = z') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IParameterReferenceExpression: z (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'z') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DynamicCollectionInitializer() + { + string source = @" +using System.Collections.Generic; + +internal class Class +{ + public dynamic X { get; set; } + + public void M(int x, int y) + { + var c = new Class() /**/{ X = { { x, y } } }/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '{ X = { { x, y } } }') + Children(1): IAssignmentExpression (OperationKind.AssignmentExpression, Type: dynamic) (Syntax: 'X = { { x, y } }') + Left: IOperation: (OperationKind.None) (Syntax: 'X') + Right: IOperation: (OperationKind.None) (Syntax: '{ { x, y } }') + Children(1): IOperation: (OperationKind.None) (Syntax: '{ x, y }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_NameOfExpression() + { + string source = @" +class Class1 +{ + public string M(int x) + { + return /**/nameof(x)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None, Constant: ""x"") (Syntax: 'nameof(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_PointerIndirectionExpression() + { + string source = @" +class Class1 +{ + public unsafe int M(int *x) + { + return /**/*x/**/; + } +} +"; + string expectedOperationTree = @" +IPointerIndirectionReferenceExpression (OperationKind.PointerIndirectionReferenceExpression, Type: System.Int32) (Syntax: '*x') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32*) (Syntax: 'x') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0227: Unsafe code may only appear if compiling with /unsafe + // public unsafe int M(int *x) + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "M").WithLocation(4, 23) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_FixedLocalInitializer() + { + string source = @" +using System.Collections.Generic; + +internal class Class +{ + public unsafe void M(int[] array) + { + fixed (int* p /**/= array/**/) + { + *p = 1; + } + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'p /**/= array') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'p /**/= array') + Variables: Local_1: System.Int32* p + Initializer: IOperation: (OperationKind.None) (Syntax: 'array') + Children(1): IParameterReferenceExpression: array (OperationKind.ParameterReferenceExpression, Type: System.Int32[]) (Syntax: 'array') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0227: Unsafe code may only appear if compiling with /unsafe + // public unsafe void M(int[] array) + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "M").WithLocation(6, 24) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_RefTypeOperator() + { + string source = @" +class Class1 +{ + public System.Type M(System.TypedReference x) + { + return /**/__reftype(x)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '__reftype(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.TypedReference) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_MakeRefOperator() + { + string source = @" +class Class1 +{ + public void M(System.Type x) + { + var y = /**/__makeref(x)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '__makeref(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Type) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_RefValueOperator() + { + string source = @" +class Class1 +{ + public void M(System.TypedReference x) + { + var y = /**/__refvalue(x, int)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '__refvalue(x, int)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.TypedReference) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DynamicIndexerAccess() + { + string source = @" +class Class1 +{ + public void M(dynamic d, int x) + { + var y /**/= d[x]/**/; + } +} +"; + string expectedOperationTree = @" +IVariableDeclarationStatement (1 declarations) (OperationKind.VariableDeclarationStatement) (Syntax: 'var y /**/;') + IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var y /**/;') + Variables: Local_1: dynamic y + Initializer: IOperation: (OperationKind.None) (Syntax: 'd[x]') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IParameterReferenceExpression: d (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'd') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DynamicMemberAccess() + { + string source = @" +class Class1 +{ + public void M(dynamic x, int y) + { + var z = /**/x.M(y)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'x.M(y)') + Children(2): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IOperation: (OperationKind.None) (Syntax: 'x.M') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DynamicInvocation() + { + string source = @" +class Class1 +{ + public void M(dynamic x, int y) + { + var z = /**/x(y)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'x(y)') + Children(2): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DynamicObjectCreation() + { + string source = @" +internal class Class +{ + public Class(Class x) { } + public Class(string x) { } + + public void M(dynamic x) + { + var c = /**/new Class(x)/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'new Class(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_StackAllocArrayCreation() + { + string source = @" +using System.Collections.Generic; + +internal class Class +{ + public unsafe void M(int x) + { + int* block = /**/stackalloc int[x]/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'stackalloc int[x]') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // CS0227: Unsafe code may only appear if compiling with /unsafe + // public unsafe void M(int x) + Diagnostic(ErrorCode.ERR_IllegalUnsafe, "M").WithLocation(6, 24) + }; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_InterpolatedStringExpression() + { + string source = @" +using System; + +internal class Class +{ + public void M(string x, int y) + { + Console.WriteLine(/**/$""String {x, 20} and {y:D3} and constant {1}""/**/); + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: '$""String {x ... nstant {1}""') + Children(6): ILiteralExpression (Text: String ) (OperationKind.LiteralExpression, Type: System.String, Constant: ""String "") (Syntax: 'String ') + IOperation: (OperationKind.None) (Syntax: '{x, 20}') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.String) (Syntax: 'x') + ILiteralExpression (Text: 20) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 20) (Syntax: '20') + ILiteralExpression (Text: and ) (OperationKind.LiteralExpression, Type: System.String, Constant: "" and "") (Syntax: ' and ') + IOperation: (OperationKind.None) (Syntax: '{y:D3}') + Children(2): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') + ILiteralExpression (OperationKind.LiteralExpression, Type: System.String, Constant: ""D3"") (Syntax: ':D3') + ILiteralExpression (Text: and constant ) (OperationKind.LiteralExpression, Type: System.String, Constant: "" and constant "") (Syntax: ' and constant ') + IOperation: (OperationKind.None) (Syntax: '{1}') + Children(1): ILiteralExpression (Text: 1) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 1) (Syntax: '1') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_ThrowExpression() + { + string source = @" +using System; + +internal class Class +{ + public void M(string x) + { + var y = x ?? /**/throw new ArgumentNullException(nameof(x))/**/; + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'throw new A ... (nameof(x))') + Children(1): IObjectCreationExpression (Constructor: System.ArgumentNullException..ctor(System.String paramName)) (OperationKind.ObjectCreationExpression, Type: System.ArgumentNullException) (Syntax: 'new Argumen ... (nameof(x))') + Arguments(1): IArgument (ArgumentKind.Explicit, Matching Parameter: paramName) (OperationKind.Argument) (Syntax: 'nameof(x)') + IOperation: (OperationKind.None, Constant: ""x"") (Syntax: 'nameof(x)') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.String) (Syntax: 'x') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_PatternSwitchStatement() + { + string source = @" +internal class Class +{ + public void M(int x) + { + switch (x) + { + /**/case var y when (x >= 10): + break;/**/ + } + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'switch (x) ... }') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'case var y ... break;') + Children(2): IOperation: (OperationKind.None) (Syntax: 'case var y ... (x >= 10):') + Children(2): IOperation: (OperationKind.None) (Syntax: 'var y') + Children(2): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') + IOperation: (OperationKind.None) (Syntax: 'var') + IBinaryOperatorExpression (BinaryOperationKind.IntegerGreaterThanOrEqual) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'x >= 10') + Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + Right: ILiteralExpression (Text: 10) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 10) (Syntax: '10') + IBranchStatement (BranchKind.Break) (OperationKind.BranchStatement) (Syntax: 'break;') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} \ No newline at end of file diff --git a/src/Compilers/Core/Portable/CodeAnalysis.csproj b/src/Compilers/Core/Portable/CodeAnalysis.csproj index e06a3696851f1..99e2386b2bfbe 100644 --- a/src/Compilers/Core/Portable/CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/CodeAnalysis.csproj @@ -98,6 +98,7 @@ + diff --git a/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs b/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs new file mode 100644 index 0000000000000..d949ba4a665da --- /dev/null +++ b/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs @@ -0,0 +1,19 @@ +// 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.Runtime.CompilerServices; + +namespace Microsoft.CodeAnalysis +{ + /// + /// Root type for nodes that are still not fully designed and hence need a backdoor to stitch the child IOperation nodes to the entire IOperation tree. + /// + /// + /// NOTE: This type is a temporary workaround and should be deleted once we ship IOperation APIs. + /// + [InternalImplementationOnly] + internal interface IOperationWithChildren: IOperation + { + ImmutableArray Children { get; } + } +} diff --git a/src/Compilers/Core/Portable/Operations/OperationWalker.cs b/src/Compilers/Core/Portable/Operations/OperationWalker.cs index 632a0780b71ad..aeed5ce40c37d 100644 --- a/src/Compilers/Core/Portable/Operations/OperationWalker.cs +++ b/src/Compilers/Core/Portable/Operations/OperationWalker.cs @@ -40,6 +40,14 @@ public override void Visit(IOperation operation) } } + internal override void VisitNoneOperation(IOperation operation) + { + if (operation is IOperationWithChildren operationWithChildren) + { + VisitArray(operationWithChildren.Children); + } + } + public override void VisitBlockStatement(IBlockStatement operation) { VisitArray(operation.Statements); diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs index f40415ee51e2c..0dee845d82b18 100644 --- a/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/SemanticModelTestBase.cs @@ -239,7 +239,7 @@ protected string GetOperationTreeForTest(CSharpCompilation compilat protected string GetOperationTreeForTest(string testSrc, string expectedOperationTree, CSharpCompilationOptions compilationOptions = null, CSharpParseOptions parseOptions = null) where TSyntaxNode : SyntaxNode { - var compilation = CreateStandardCompilation(testSrc, new[] { SystemCoreRef }, options: compilationOptions ?? TestOptions.ReleaseDll, parseOptions: parseOptions); + var compilation = CreateStandardCompilation(testSrc, new[] { SystemCoreRef, ValueTupleRef, SystemRuntimeFacadeRef }, options: compilationOptions ?? TestOptions.ReleaseDll, parseOptions: parseOptions); return GetOperationTreeForTest(compilation); } @@ -268,7 +268,7 @@ protected void VerifyOperationTreeAndDiagnosticsForTest(CSharpCompi protected void VerifyOperationTreeAndDiagnosticsForTest(string testSrc, string expectedOperationTree, DiagnosticDescription[] expectedDiagnostics, CSharpCompilationOptions compilationOptions = null, CSharpParseOptions parseOptions = null) where TSyntaxNode : SyntaxNode { - var compilation = CreateStandardCompilation(testSrc, new[] { SystemCoreRef }, options: compilationOptions ?? TestOptions.ReleaseDll, parseOptions: parseOptions); + var compilation = CreateStandardCompilation(testSrc, new[] { SystemCoreRef, ValueTupleRef, SystemRuntimeFacadeRef }, options: compilationOptions ?? TestOptions.ReleaseDll, parseOptions: parseOptions); VerifyOperationTreeAndDiagnosticsForTest(compilation, expectedOperationTree, expectedDiagnostics); } } diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index 304adec7cc5a8..d11e23aad6750 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -11,6 +11,7 @@ using Microsoft.CodeAnalysis.Test.Extensions; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.Test.Utilities @@ -255,6 +256,11 @@ internal override void VisitNoneOperation(IOperation operation) { LogString("IOperation: "); LogCommonPropertiesAndNewLine(operation); + + if (operation is IOperationWithChildren operationWithChildren && operationWithChildren.Children.Length > 0) + { + VisitArray(operationWithChildren.Children.WhereNotNull().ToImmutableArray(), "Children", logElementCount: true); + } } public override void VisitBlockStatement(IBlockStatement operation) From 6ecf1570c94abb554282fc87519685ab52a54982 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 26 Apr 2017 15:24:29 -0700 Subject: [PATCH 016/214] VB side changes and unit tests for IOperationWithChildren --- .../CSharp/Portable/BoundTree/Expression.cs | 8 +- .../CSharp/Portable/BoundTree/Statement.cs | 2 +- .../VisualBasic/SemanticModelTestBase.vb | 4 +- .../Portable/BoundTree/BoundNode.vb | 2 +- .../Portable/BoundTree/Expression.vb | 429 +++++++++++++----- .../Portable/BoundTree/Statement.vb | 130 +++++- .../Semantic/BasicCompilerSemanticTest.vbproj | 1 + ...tionTests_IParameterReferenceExpression.vb | 404 +++++++++++++++++ .../Compilation/OperationTreeVerifier.cs | 8 +- 9 files changed, 851 insertions(+), 137 deletions(-) create mode 100644 src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 4fe0c3c5a38cc..e22bc641c8d93 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -1420,7 +1420,7 @@ internal partial class BoundDynamicIndexerAccess { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.Concat(this.ReceiverOpt).As(); + protected override ImmutableArray Children => this.Arguments.Add(this.ReceiverOpt).As(); public override void Accept(OperationVisitor visitor) { @@ -1486,7 +1486,7 @@ internal partial class BoundAttribute { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.ConstructorArguments.Concat(this.NamedArguments).As(); + protected override ImmutableArray Children => this.ConstructorArguments.AddRange(this.NamedArguments).As(); public override void Accept(OperationVisitor visitor) { @@ -1873,7 +1873,7 @@ internal partial class BoundDynamicInvocation { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.As().Concat(this.Expression); + protected override ImmutableArray Children => this.Arguments.As().Add(this.Expression); public override void Accept(OperationVisitor visitor) { @@ -2140,7 +2140,7 @@ internal partial class BoundDynamicObjectCreationExpression { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.Concat(BoundObjectCreationExpression.GetChildInitializers(this.InitializerExpressionOpt)).As(); + protected override ImmutableArray Children => this.Arguments.AddRange(BoundObjectCreationExpression.GetChildInitializers(this.InitializerExpressionOpt)).As(); public override void Accept(OperationVisitor visitor) { diff --git a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs index 57ea3ecc34bc2..2fe6374dd1d8e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs @@ -904,7 +904,7 @@ public override TResult Accept(OperationVisitor Children => this.SwitchLabels.As().Concat(this.Statements).ToImmutableArray(); + protected override ImmutableArray Children => this.SwitchLabels.As().AddRange(this.Statements).ToImmutableArray(); } partial class BoundPatternSwitchLabel diff --git a/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb b/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb index 0ec9775d00131..0e7f292fd9e34 100644 --- a/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb +++ b/src/Compilers/Test/Utilities/VisualBasic/SemanticModelTestBase.vb @@ -169,7 +169,7 @@ Public MustInherit Class SemanticModelTestBase : Inherits BasicTestBase Friend Function GetOperationTreeForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, Optional compilationOptions As VisualBasicCompilationOptions = Nothing, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) As String Dim fileName = "a.vb" Dim syntaxTree = Parse(testSrc, fileName, parseOptions) - Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime({syntaxTree}, references:=DefaultVbReferences, options:=If(compilationOptions, TestOptions.ReleaseDll)) + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime({syntaxTree}, references:=DefaultVbReferences.Append({ValueTupleRef, SystemRuntimeFacadeRef}), options:=If(compilationOptions, TestOptions.ReleaseDll)) Return GetOperationTreeForTest(Of TSyntaxNode)(compilation, fileName, which) End Function @@ -196,7 +196,7 @@ Public MustInherit Class SemanticModelTestBase : Inherits BasicTestBase Friend Sub VerifyOperationTreeAndDiagnosticsForTest(Of TSyntaxNode As SyntaxNode)(testSrc As String, expectedOperationTree As String, expectedDiagnostics As String, Optional compilationOptions As VisualBasicCompilationOptions = Nothing, Optional parseOptions As VisualBasicParseOptions = Nothing, Optional which As Integer = 0) Dim fileName = "a.vb" Dim syntaxTree = Parse(testSrc, fileName, parseOptions) - Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime({syntaxTree}, references:=DefaultVbReferences, options:=If(compilationOptions, TestOptions.ReleaseDll)) + Dim compilation = CreateCompilationWithMscorlib45AndVBRuntime({syntaxTree}, references:=DefaultVbReferences.Append({ValueTupleRef, SystemRuntimeFacadeRef}), options:=If(compilationOptions, TestOptions.ReleaseDll)) VerifyOperationTreeAndDiagnosticsForTest(Of TSyntaxNode)(compilation, fileName, expectedOperationTree, expectedDiagnostics, which) End Sub diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNode.vb b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNode.vb index 0dd83563ad6f5..6f99e639a91b0 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/BoundNode.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/BoundNode.vb @@ -93,7 +93,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Get End Property - Public Overridable Function Accept(visitor As BoundTreeVisitor) As BoundNode + Public Overridable Overloads Function Accept(visitor As BoundTreeVisitor) As BoundNode Throw ExceptionUtilities.Unreachable End Function diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index 995b04c68dbc7..7d29acc0ff28f 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -7,48 +7,34 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class BoundExpression - Implements IOperation - - Private ReadOnly Property IOperation_ConstantValue As [Optional](Of Object) Implements IOperation.ConstantValue + Protected Overrides ReadOnly Property OperationKind As OperationKind Get - Dim value As ConstantValue = Me.ConstantValueOpt - If value Is Nothing Then - Return New [Optional](Of Object)() - End If - - Return New [Optional](Of Object)(value.Value) + Return Me.ExpressionKind End Get End Property - Private ReadOnly Property IOperation_Kind As OperationKind Implements IOperation.Kind - Get - Return Me.ExpressionKind() - End Get - End Property - - Private ReadOnly Property IOperation_IsInvalid As Boolean Implements IOperation.IsInvalid - Get - Return Me.HasErrors - End Get - End Property - - Private ReadOnly Property IOperation_Type As ITypeSymbol Implements IOperation.Type + Protected Overrides ReadOnly Property OperationType As ITypeSymbol Get Return Me.Type End Get End Property - Private ReadOnly Property IOperation_Syntax As SyntaxNode Implements IOperation.Syntax + Protected Overrides ReadOnly Property OperationConstantValue As [Optional](Of Object) Get - Return Me.Syntax + Dim value As ConstantValue = Me.ConstantValueOpt + If value Is Nothing Then + Return New [Optional](Of Object)() + End If + + Return New [Optional](Of Object)(value.Value) End Get End Property Protected MustOverride Function ExpressionKind() As OperationKind - Public MustOverride Overloads Sub Accept(visitor As OperationVisitor) Implements IOperation.Accept + Public MustOverride Overloads Overrides Sub Accept(visitor As OperationVisitor) - Public MustOverride Overloads Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult Implements IOperation.Accept + Public MustOverride Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult Protected Shared Function GetChildOfBadExpression(parent As BoundNode, index As Integer) As IOperation Dim badParent As BoundBadExpression = TryCast(parent, BoundBadExpression) @@ -61,7 +47,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return New InvalidExpression(parent.Syntax, ImmutableArray(Of IOperation).Empty) End Function - End Class Partial Friend Class BoundAssignmentOperator @@ -902,7 +887,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class BoundBadExpression Implements IInvalidExpression - Public ReadOnly Property Children As ImmutableArray(Of IOperation) Implements IInvalidExpression.Children + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Implements IInvalidExpression.Children Get Return StaticCast(Of IOperation).From(Me.ChildBoundNodes) End Get @@ -921,7 +906,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTryCast + Partial Friend Class BoundTryCast Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -967,7 +952,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundDirectCast + Partial Friend Class BoundDirectCast Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -1013,7 +998,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundConversion + Partial Friend Class BoundConversion Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -1059,7 +1044,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundUserDefinedConversion + Partial Friend Class BoundUserDefinedConversion Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -1105,7 +1090,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTernaryConditionalExpression + Partial Friend Class BoundTernaryConditionalExpression Implements IConditionalChoiceExpression Private ReadOnly Property IConditionalChoiceExpression_Condition As IOperation Implements IConditionalChoiceExpression.Condition @@ -1139,7 +1124,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTypeOf + Partial Friend Class BoundTypeOf Implements IIsTypeExpression Private ReadOnly Property IIsTypeExpression_IsType As ITypeSymbol Implements IIsTypeExpression.IsType @@ -1167,7 +1152,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundObjectCreationExpression + Partial Friend Class BoundObjectCreationExpression Implements IObjectCreationExpression Private Shared ReadOnly s_memberInitializersMappings As New System.Runtime.CompilerServices.ConditionalWeakTable(Of BoundObjectCreationExpression, Object) @@ -1371,7 +1356,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundArrayCreation + Partial Friend Class BoundArrayCreation Implements IArrayCreationExpression Private ReadOnly Property IArrayCreationExpression_DimensionSizes As ImmutableArray(Of IOperation) Implements IArrayCreationExpression.DimensionSizes @@ -1411,7 +1396,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundArrayInitialization + Partial Friend Class BoundArrayInitialization Implements IArrayInitializer Private ReadOnly Property IArrayInitializer_ElementValues As ImmutableArray(Of IOperation) Implements IArrayInitializer.ElementValues @@ -1432,7 +1417,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundPropertyAccess + Partial Friend Class BoundPropertyAccess Implements IIndexedPropertyReferenceExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1476,7 +1461,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundEventAccess + Partial Friend Class BoundEventAccess Implements IEventReferenceExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1514,7 +1499,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundDelegateCreationExpression + Partial Friend Class BoundDelegateCreationExpression Implements IMethodBindingExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1558,7 +1543,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundFieldAccess + Partial Friend Class BoundFieldAccess Implements IFieldReferenceExpression Private ReadOnly Property IFieldReferenceExpression_Field As IFieldSymbol Implements IFieldReferenceExpression.Field @@ -1596,7 +1581,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundConditionalAccess + Partial Friend Class BoundConditionalAccess Implements IConditionalAccessExpression Private ReadOnly Property IConditionalAccessExpression_ConditionalValue As IOperation Implements IConditionalAccessExpression.ConditionalValue @@ -1640,7 +1625,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundParameter + Partial Friend Class BoundParameter Implements IParameterReferenceExpression Private ReadOnly Property IParameterReferenceExpression_Parameter As IParameterSymbol Implements IParameterReferenceExpression.Parameter @@ -1662,7 +1647,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLocal + Partial Friend Class BoundLocal Implements ILocalReferenceExpression Private ReadOnly Property ILocalReferenceExpression_Local As ILocalSymbol Implements ILocalReferenceExpression.Local @@ -1684,7 +1669,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLateMemberAccess + Partial Friend Class BoundLateMemberAccess Implements ILateBoundMemberReferenceExpression Private ReadOnly Property ILateBoundMemberReferenceExpression_Instance As IOperation Implements ILateBoundMemberReferenceExpression.Instance @@ -1712,7 +1697,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundFieldInitializer + Partial Friend Class BoundFieldInitializer Implements IFieldInitializer Private ReadOnly Property IFieldInitializer_InitializedFields As ImmutableArray(Of IFieldSymbol) Implements IFieldInitializer.InitializedFields @@ -1740,7 +1725,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundPropertyInitializer + Partial Friend Class BoundPropertyInitializer Implements IPropertyInitializer Private ReadOnly Property IPropertyInitializer_InitializedProperty As IPropertySymbol Implements IPropertyInitializer.InitializedProperty @@ -1768,7 +1753,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundParameterEqualsValue + Partial Friend Class BoundParameterEqualsValue Implements IParameterInitializer Private ReadOnly Property IOperation_IsInvalid As Boolean Implements IOperation.IsInvalid @@ -1822,7 +1807,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTypeArguments + Partial Friend Class BoundTypeArguments Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1836,11 +1821,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLValueToRValueWrapper + Partial Friend Class BoundLValueToRValueWrapper Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.UnderlyingLValue) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1850,7 +1841,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundWithLValueExpressionPlaceholder + Partial Friend Class BoundWithLValueExpressionPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1864,7 +1855,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundWithRValueExpressionPlaceholder + Partial Friend Class BoundWithRValueExpressionPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1894,7 +1885,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLValuePlaceholder + Partial Friend Class BoundLValuePlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1908,7 +1899,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundDup + Partial Friend Class BoundDup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1922,7 +1913,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundBadVariable + Partial Friend Class BoundBadVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1936,11 +1927,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundArrayLength + Partial Friend Class BoundArrayLength Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Expression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1950,11 +1947,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundGetType + Partial Friend Class BoundGetType Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.SourceType) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1964,7 +1967,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundFieldInfo + Partial Friend Class BoundFieldInfo Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1978,7 +1981,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundMethodInfo + Partial Friend Class BoundMethodInfo Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1992,7 +1995,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTypeExpression + Partial Friend Class BoundTypeExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2006,7 +2009,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTypeOrValueExpression + Partial Friend Class BoundTypeOrValueExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2020,7 +2023,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundNamespaceExpression + Partial Friend Class BoundNamespaceExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2034,11 +2037,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundNullableIsTrueOperator + Partial Friend Class BoundNullableIsTrueOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Operand) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2048,7 +2057,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundCompoundAssignmentTargetPlaceholder + Partial Friend Class BoundCompoundAssignmentTargetPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2062,11 +2071,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundReferenceAssignment + Partial Friend Class BoundReferenceAssignment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.LValue) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2076,7 +2091,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAddressOfOperator + Partial Friend Class BoundAddressOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2090,7 +2105,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundSequencePointExpression + Partial Friend Class BoundSequencePointExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2104,7 +2119,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundMethodGroup + Partial Friend Class BoundMethodGroup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2118,7 +2133,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundPropertyGroup + Partial Friend Class BoundPropertyGroup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2132,11 +2147,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAttribute + Partial Friend Class BoundAttribute Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.ConstructorArguments.AddRange(Me.NamedArguments).As(Of IOperation)() + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2151,6 +2172,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Arguments.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2160,11 +2187,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLateInvocation + Partial Friend Class BoundLateInvocation Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.ArgumentsOpt.Insert(0, Me.Member).As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2174,11 +2207,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLateAddressOfOperator + Partial Friend Class BoundLateAddressOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.MemberAccess) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2188,11 +2227,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundNoPiaObjectCreationExpression + Partial Friend Class BoundNoPiaObjectCreationExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return If(InitializerOpt IsNot Nothing, InitializerOpt.Initializers.As(Of IOperation), ImmutableArray(Of IOperation).Empty) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2202,11 +2247,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAnonymousTypeCreationExpression + Partial Friend Class BoundAnonymousTypeCreationExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Arguments.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2216,7 +2267,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAnonymousTypePropertyAccess + Partial Friend Class BoundAnonymousTypePropertyAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2230,11 +2281,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAnonymousTypeFieldInitializer + Partial Friend Class BoundAnonymousTypeFieldInitializer Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Value) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2244,11 +2301,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundObjectInitializerExpression + Partial Friend Class BoundObjectInitializerExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Initializers.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2258,11 +2321,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundCollectionInitializerExpression + Partial Friend Class BoundCollectionInitializerExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Initializers.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2272,11 +2341,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundArrayLiteral + Partial Friend Class BoundArrayLiteral Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Bounds.Add(Me.Initializer).As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2286,7 +2361,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundSequence + Partial Friend Class BoundSequence Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2300,7 +2375,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundValueTypeMeReference + Partial Friend Class BoundValueTypeMeReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2314,7 +2389,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundPreviousSubmissionReference + Partial Friend Class BoundPreviousSubmissionReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2328,7 +2403,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundHostObjectMemberReference + Partial Friend Class BoundHostObjectMemberReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2342,7 +2417,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundPseudoVariable + Partial Friend Class BoundPseudoVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2356,7 +2431,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundByRefArgumentPlaceholder + Partial Friend Class BoundByRefArgumentPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2370,11 +2445,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundByRefArgumentWithCopyBack + Partial Friend Class BoundByRefArgumentWithCopyBack Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.OriginalArgument, Me.InConversion, Me.InPlaceholder, Me.OutConversion, Me.OutPlaceholder) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2384,11 +2465,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLateBoundArgumentSupportingAssignmentWithCapture + Partial Friend Class BoundLateBoundArgumentSupportingAssignmentWithCapture Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.OriginalArgument) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2398,7 +2485,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLabel + Partial Friend Class BoundLabel Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2412,7 +2499,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class UnboundLambda + Partial Friend Class UnboundLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2426,11 +2513,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundQueryExpression + Partial Friend Class BoundQueryExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.LastOperator) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2440,11 +2533,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundQuerySource + Partial Friend Class BoundQueryPart + Protected MustOverride Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + End Class + + Partial Friend Class BoundQuerySource Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Expression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2454,11 +2557,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundToQueryableCollectionConversion + Partial Friend Class BoundToQueryableCollectionConversion Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.ConversionCall) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2468,11 +2577,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundQueryableSource + Partial Friend Class BoundQueryableSource Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Source) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2482,11 +2597,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundQueryClause + Partial Friend Class BoundQueryClause Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.UnderlyingExpression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2496,11 +2617,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundOrdering + Partial Friend Class BoundOrdering Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.UnderlyingExpression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2510,11 +2637,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundQueryLambda + Partial Friend Class BoundQueryLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Expression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2524,11 +2657,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundRangeVariableAssignment + Partial Friend Class BoundRangeVariableAssignment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Value) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2538,7 +2677,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class GroupTypeInferenceLambda + Partial Friend Class GroupTypeInferenceLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2552,11 +2691,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundAggregateClause + Partial Friend Class BoundAggregateClause Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.CapturedGroupOpt, Me.GroupPlaceholderOpt, Me.UnderlyingExpression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2566,11 +2711,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundGroupAggregation + Partial Friend Class BoundGroupAggregation Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Group) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2580,7 +2731,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundRangeVariable + Partial Friend Class BoundRangeVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2594,7 +2745,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlName + Partial Friend Class BoundXmlName Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2608,7 +2759,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlNamespace + Partial Friend Class BoundXmlNamespace Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2622,7 +2773,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlDocument + Partial Friend Class BoundXmlDocument Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2636,7 +2787,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlDeclaration + Partial Friend Class BoundXmlDeclaration Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2650,7 +2801,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlProcessingInstruction + Partial Friend Class BoundXmlProcessingInstruction Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2664,7 +2815,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlComment + Partial Friend Class BoundXmlComment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2678,7 +2829,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlAttribute + Partial Friend Class BoundXmlAttribute Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2692,7 +2843,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlElement + Partial Friend Class BoundXmlElement Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2706,7 +2857,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlMemberAccess + Partial Friend Class BoundXmlMemberAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2720,7 +2871,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlEmbeddedExpression + Partial Friend Class BoundXmlEmbeddedExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2734,7 +2885,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundXmlCData + Partial Friend Class BoundXmlCData Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2748,7 +2899,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundUnstructuredExceptionHandlingCatchFilter + Partial Friend Class BoundUnstructuredExceptionHandlingCatchFilter Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2762,7 +2913,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundSpillSequence + Partial Friend Class BoundSpillSequence Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2776,11 +2927,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundMidResult + Partial Friend Class BoundMidResult Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Original, Me.Start, Me.LengthOpt, Me.Source) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2790,7 +2947,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundLoweredConditionalAccess + Partial Friend Class BoundLoweredConditionalAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2804,11 +2961,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundComplexConditionalAccessReceiver + Partial Friend Class BoundComplexConditionalAccessReceiver Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.ValueTypeReceiver, Me.ReferenceTypeReceiver) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2818,11 +2981,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundNameOfOperator + Partial Friend Class BoundNameOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Argument) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2832,11 +3001,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundTypeAsValueExpression + Partial Friend Class BoundTypeAsValueExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Expression) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2846,11 +3021,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Friend Partial Class BoundInterpolatedStringExpression + Partial Friend Class BoundInterpolatedStringExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Contents.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2860,6 +3041,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class + Partial Friend Class BoundInterpolation + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Expression, Me.AlignmentOpt, Me.FormatStringOpt) + End Get + End Property + End Class + Partial Friend Class BoundModuleVersionId Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb index fb6f588a53d1d..907065cf462c7 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb @@ -6,12 +6,12 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic - Partial Friend Class BoundStatement - Implements IOperation + Partial Friend Class BoundNode + Implements IOperation, IOperationWithChildren Private ReadOnly Property IOperation_Kind As OperationKind Implements IOperation.Kind Get - Return Me.StatementKind() + Return Me.OperationKind End Get End Property @@ -29,21 +29,71 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Private ReadOnly Property IOperation_Type As ITypeSymbol Implements IOperation.Type Get - Return Nothing + Return Me.OperationType End Get End Property Private ReadOnly Property IOperation_ConstantValue As [Optional](Of Object) Implements IOperation.ConstantValue + Get + Return Me.OperationConstantValue + End Get + End Property + + Public ReadOnly Property IOperationWithChildren_Children As ImmutableArray(Of IOperation) Implements IOperationWithChildren.Children + Get + Return Me.Children + End Get + End Property + + Protected Overridable ReadOnly Property OperationKind As OperationKind + Get + Return OperationKind.None + End Get + End Property + + Protected Overridable ReadOnly Property OperationType As ITypeSymbol + Get + Return Nothing + End Get + End Property + + Protected Overridable ReadOnly Property OperationConstantValue As [Optional](Of Object) Get Return New [Optional](Of Object)() End Get End Property + ''' + ''' Override this property to return the child operations if the IOperation API corresponding to this bound node is not yet designed or implemented. + ''' + Protected Overridable ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray(Of IOperation).Empty + End Get + End Property + + Public Overridable Overloads Sub Accept(visitor As OperationVisitor) Implements IOperation.Accept + visitor.VisitNoneOperation(Me) + End Sub + + Public Overridable Overloads Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult Implements IOperation.Accept + visitor.VisitNoneOperation(Me, argument) + End Function + End Class + + Partial Friend Class BoundStatement + + Protected Overrides ReadOnly Property OperationKind As OperationKind + Get + Return Me.StatementKind + End Get + End Property + Protected MustOverride Function StatementKind() As OperationKind - Public MustOverride Overloads Sub Accept(visitor As OperationVisitor) Implements IOperation.Accept + Public MustOverride Overloads Overrides Sub Accept(visitor As OperationVisitor) - Public MustOverride Overloads Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult Implements IOperation.Accept + Public MustOverride Overloads Overrides Function Accept(Of TArgument, TResult)(visitor As OperationVisitor(Of TArgument, TResult), argument As TArgument) As TResult End Class Partial Friend Class BoundIfStatement @@ -261,6 +311,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.CaseStatement, Me.Body) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -486,6 +542,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.CaseClauses.As(Of IOperation).Add(Me.ConditionOpt) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -943,7 +1005,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Partial Friend Class BoundBadStatement Implements IInvalidStatement - Public ReadOnly Property Children As ImmutableArray(Of IOperation) Implements IInvalidStatement.Children + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Implements IInvalidStatement.Children Get Dim builder As ArrayBuilder(Of IOperation) = ArrayBuilder(Of IOperation).GetInstance(Me.ChildBoundNodes.Length) For Each childNode In Me.ChildBoundNodes @@ -1630,6 +1692,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Clauses.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1644,6 +1712,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Indices.As(Of IOperation).Insert(0, Me.Operand) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1658,6 +1732,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Clauses.As(Of IOperation) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1714,6 +1794,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Condition) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1764,6 +1850,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.LabelExpressionOpt) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1778,6 +1870,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.LabelExpressionOpt) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1792,6 +1890,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return ImmutableArray.Create(Of IOperation)(Me.Body) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1806,6 +1910,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Jumps.As(Of IOperation).Insert(0, Me.Value) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1820,6 +1930,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function + Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) + Get + Return Me.Jumps.As(Of IOperation).InsertRange(0, {Me.ResumeLabel, Me.ResumeNextLabel}) + End Get + End Property + Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index 4a44eebd9c104..729cc4fb34930 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -97,6 +97,7 @@ + diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb new file mode 100644 index 0000000000000..4cc0c70da8f3f --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb @@ -0,0 +1,404 @@ +' 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 Microsoft.CodeAnalysis.Semantics +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + Public Sub ParameterReference_TupleExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of TupleExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_AnonymousObjectCreation() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AnonymousObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_QueryExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_ObjectAndCollectionInitializer() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_NameOfExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of NameOfExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_LateBoundIndexerAccess() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of InvocationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_LateBoundMemberAccess() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of InvocationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_LateBoundInvocation() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of InvocationExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_InterpolatedStringExpression() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of InterpolatedStringExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_MidAssignmentStatement() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AssignmentStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_MisplacedCaseStatement() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of CaseStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_RedimStatement() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of ReDimStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_EraseStatement() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of EraseStatementSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_UnstructuredExceptionHandlingStatement() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of MethodBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs index d11e23aad6750..95209b461d02a 100644 --- a/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs +++ b/src/Test/Utilities/Portable/Compilation/OperationTreeVerifier.cs @@ -257,9 +257,13 @@ internal override void VisitNoneOperation(IOperation operation) LogString("IOperation: "); LogCommonPropertiesAndNewLine(operation); - if (operation is IOperationWithChildren operationWithChildren && operationWithChildren.Children.Length > 0) + if (operation is IOperationWithChildren operationWithChildren) { - VisitArray(operationWithChildren.Children.WhereNotNull().ToImmutableArray(), "Children", logElementCount: true); + var children = operationWithChildren.Children.WhereNotNull().ToImmutableArray(); + if (children.Length > 0) + { + VisitArray(children, "Children", logElementCount: true); + } } } From 62bba7f8cc76404ec7f0306b91092c75bd937bcb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 18:56:21 -0700 Subject: [PATCH 017/214] Expose 'use expression body' as an analyzer and a refactoring. --- .../CSharp/Portable/CSharpFeatures.csproj | 52 ++++-- ...ractUseExpressionBodyDiagnosticAnalyzer.cs | 172 ------------------ ...pressionBodyForAccessorsCodeFixProvider.cs | 43 ----- ...ssionBodyForAccessorsDiagnosticAnalyzer.cs | 61 ------- ...ractUseExpressionBodyDiagnosticAnalyzer.cs | 93 ++++++++++ ...ssionBodyForAccessorsDiagnosticAnalyzer.cs | 23 +++ ...onBodyForConstructorsDiagnosticAnalyzer.cs | 11 +- ...orConversionOperatorsDiagnosticAnalyzer.cs | 11 +- ...essionBodyForIndexersDiagnosticAnalyzer.cs | 11 +- ...ressionBodyForMethodsDiagnosticAnalyzer.cs | 11 +- ...ssionBodyForOperatorsDiagnosticAnalyzer.cs | 11 +- ...sionBodyForPropertiesDiagnosticAnalyzer.cs | 11 +- ...bstractUseExpressionBodyCodeFixProvider.cs | 88 +++++++++ ...pressionBodyForAccessorsCodeFixProvider.cs | 20 ++ ...ssionBodyForConstructorsCodeFixProvider.cs | 19 ++ ...dyForConversionOperatorsCodeFixProvider.cs | 19 ++ ...xpressionBodyForIndexersCodeFixProvider.cs | 22 +++ ...ExpressionBodyForMethodsCodeFixProvider.cs | 19 ++ ...pressionBodyForOperatorsCodeFixProvider.cs | 19 ++ ...ressionBodyForPropertiesCodeFixProvider.cs | 19 ++ ...seExpressionBodyCodeRefactoringProvider.cs | 99 ++++++++++ ...BodyForAccessorsCodeRefactoringProvider.cs | 17 ++ ...yForConstructorsCodeRefactoringProvider.cs | 17 ++ ...versionOperatorsCodeRefactoringProvider.cs | 17 ++ ...nBodyForIndexersCodeRefactoringProvider.cs | 17 ++ ...onBodyForMethodsCodeRefactoringProvider.cs | 17 ++ ...BodyForOperatorsCodeRefactoringProvider.cs | 17 ++ ...odyForPropertiesCodeRefactoringProvider.cs | 17 ++ .../AbstractUseExpressionBodyHelper.cs} | 133 +++++++------- .../UseExpressionBodyForAccessorsHelper.cs | 100 ++++++++++ ...UseExpressionBodyForConstructorsHelper.cs} | 26 ++- ...essionBodyForConversionOperatorsHelper.cs} | 27 ++- .../UseExpressionBodyForIndexersHelper.cs} | 28 ++- .../UseExpressionBodyForMethodsHelper.cs} | 27 ++- .../UseExpressionBodyForOperatorsHelper.cs} | 27 ++- .../UseExpressionBodyForPropertiesHelper.cs} | 30 ++- 36 files changed, 836 insertions(+), 515 deletions(-) delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs rename src/Features/CSharp/Portable/UseExpressionBody/{Constructors => Analyzers}/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs (51%) rename src/Features/CSharp/Portable/UseExpressionBody/{Operators => Analyzers}/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs (52%) rename src/Features/CSharp/Portable/UseExpressionBody/{Indexers => Analyzers}/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs (50%) rename src/Features/CSharp/Portable/UseExpressionBody/{Methods => Analyzers}/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs (51%) rename src/Features/CSharp/Portable/UseExpressionBody/{Operators => Analyzers}/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs (51%) rename src/Features/CSharp/Portable/UseExpressionBody/{Properties => Analyzers}/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs (50%) create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs rename src/Features/CSharp/Portable/UseExpressionBody/{AbstractUseExpressionBodyCodeFixProvider.cs => Helpers/AbstractUseExpressionBodyHelper.cs} (54%) create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs rename src/Features/CSharp/Portable/UseExpressionBody/{Constructors/UseExpressionBodyForConstructorsCodeFixProvider.cs => Helpers/UseExpressionBodyForConstructorsHelper.cs} (62%) rename src/Features/CSharp/Portable/UseExpressionBody/{Operators/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs => Helpers/UseExpressionBodyForConversionOperatorsHelper.cs} (62%) rename src/Features/CSharp/Portable/UseExpressionBody/{Indexers/UseExpressionBodyForIndexersCodeFixProvider.cs => Helpers/UseExpressionBodyForIndexersHelper.cs} (67%) rename src/Features/CSharp/Portable/UseExpressionBody/{Methods/UseExpressionBodyForMethodsCodeFixProvider.cs => Helpers/UseExpressionBodyForMethodsHelper.cs} (64%) rename src/Features/CSharp/Portable/UseExpressionBody/{Operators/UseExpressionBodyForOperatorsCodeFixProvider.cs => Helpers/UseExpressionBodyForOperatorsHelper.cs} (62%) rename src/Features/CSharp/Portable/UseExpressionBody/{Properties/UseExpressionBodyForPropertiesCodeFixProvider.cs => Helpers/UseExpressionBodyForPropertiesHelper.cs} (67%) diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 653a38493d46f..060ceb34a6e74 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -96,18 +96,34 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -403,10 +419,10 @@ - - - - + + + + @@ -488,7 +504,9 @@ - + + + \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyDiagnosticAnalyzer.cs deleted file mode 100644 index 778d9f0565173..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyDiagnosticAnalyzer.cs +++ /dev/null @@ -1,172 +0,0 @@ -// 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.Immutable; -using Microsoft.CodeAnalysis.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Extensions; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - internal abstract class AbstractUseExpressionBodyDiagnosticAnalyzer : - AbstractCodeStyleDiagnosticAnalyzer - where TDeclaration : SyntaxNode - { - private readonly ImmutableArray _syntaxKinds; - private readonly Option> _option; - private readonly LocalizableString _expressionBodyTitle; - private readonly LocalizableString _blockBodyTitle; - - public override bool OpenFileOnly(Workspace workspace) => false; - - protected AbstractUseExpressionBodyDiagnosticAnalyzer( - string diagnosticId, - LocalizableString expressionBodyTitle, - LocalizableString blockBodyTitle, - ImmutableArray syntaxKinds, - Option> option) - : base(diagnosticId, expressionBodyTitle) - { - _syntaxKinds = syntaxKinds; - _option = option; - _expressionBodyTitle = expressionBodyTitle; - _blockBodyTitle = blockBodyTitle; - } - - public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; - - protected override void InitializeWorker(AnalysisContext context) - => context.RegisterSyntaxNodeAction(AnalyzeSyntax, _syntaxKinds); - - private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) - { - var options = context.Options; - var syntaxTree = context.Node.SyntaxTree; - var cancellationToken = context.CancellationToken; - var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); - if (optionSet == null) - { - return; - } - - var diagnostic = AnalyzeSyntax(optionSet, (TDeclaration)context.Node); - if (diagnostic != null) - { - context.ReportDiagnostic(diagnostic); - } - } - - internal virtual Diagnostic AnalyzeSyntax(OptionSet optionSet, TDeclaration declaration) - { - // Note: we will always offer to convert a block to an expression-body (and vice versa) - // if possible. All the user preference does is determine if we show them anything in - // the UI (i.e. suggestion dots, or a squiggle) to let them know that they can make the - // change. - // - // This way, users can turn off the option so they don't get notified, but they can still - // make the transformation on a case by case basis. - // - // Note: if we decide to hide any adornments, then we also broaden the location where the - // fix is available. That way the user can go to any place in the member and choose to - // convert it. Otherwise, they'd have no idea what the 'right' location was to invoke - // the conversion. - // - // Also, if the diagnostic is hidden, we'll lower the priority of the code action. We - // always want it to be available. But we don't want it to override issues that are - // actually being reported in the UI. - - var preferExpressionBodiedOption = optionSet.GetOption(_option); - - var expressionBody = GetExpressionBody(declaration); - - if (expressionBody == null) - { - // They don't have an expression body. See if we can convert into one. - // If so, offer the conversion (with the proper severity depending on their options). - var options = declaration.SyntaxTree.Options; - var body = GetBody(declaration); - if (body.TryConvertToExpressionBody(options, ExpressionBodyPreference.WhenOnSingleLine, out var expressionWhenOnSingleLine, out var semicolonWhenOnSingleLine)) - { - // See if it can be converted to an expression and is on a single line. If so, - // we'll show the diagnostic if either 'use expression body' preference is set. - var severity = - preferExpressionBodiedOption.Value == ExpressionBodyPreference.WhenOnSingleLine || preferExpressionBodiedOption.Value == ExpressionBodyPreference.WhenPossible - ? preferExpressionBodiedOption.Notification.Value - : DiagnosticSeverity.Hidden; - - return GetDiagnostic(declaration, severity); - } - - if (body.TryConvertToExpressionBody(options, ExpressionBodyPreference.WhenPossible, out var expressionWhenPossible, out var semicolonWhenPossible)) - { - // It wasn't an expression that was on a single line. But it was something we - // could convert to an expression body. We'll show the diagnostic only if - // the option to report when possible is set. - var severity = - preferExpressionBodiedOption.Value == ExpressionBodyPreference.WhenPossible - ? preferExpressionBodiedOption.Notification.Value - : DiagnosticSeverity.Hidden; - - return GetDiagnostic(declaration, severity); - } - - // Can't be converted. - return null; - } - else - { - // They have an expression body. These can always be converted into blocks. - // Offer to convert this to a block, with the appropriate severity based - // on their options. - var severity = preferExpressionBodiedOption.Value != ExpressionBodyPreference.Never - ? DiagnosticSeverity.Hidden - : preferExpressionBodiedOption.Notification.Value; - - var location = severity == DiagnosticSeverity.Hidden - ? declaration.GetLocation() - : expressionBody.GetLocation(); - - var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); - return Diagnostic.Create( - CreateDescriptorWithTitle(_blockBodyTitle, severity, GetCustomTags(severity)), - location, additionalLocations: additionalLocations); - } - } - - private Diagnostic GetDiagnostic(TDeclaration declaration, DiagnosticSeverity severity) - { - var location = severity == DiagnosticSeverity.Hidden - ? declaration.GetLocation() - : GetBody(declaration).Statements[0].GetLocation(); - - var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); - return Diagnostic.Create( - CreateDescriptorWithTitle(_expressionBodyTitle, severity, GetCustomTags(severity)), - location, additionalLocations: additionalLocations); - } - - private static string[] GetCustomTags(DiagnosticSeverity severity) - => severity == DiagnosticSeverity.Hidden - ? new[] { WellKnownDiagnosticTags.NotConfigurable } - : Array.Empty(); - - protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList) - { - if (accessorList != null && - accessorList.Accessors.Count == 1 && - accessorList.Accessors[0].AttributeLists.Count == 0 && - accessorList.Accessors[0].IsKind(SyntaxKind.GetAccessorDeclaration)) - { - return accessorList.Accessors[0].Body; - } - - return null; - } - - protected abstract BlockSyntax GetBody(TDeclaration declaration); - protected abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration); - } -} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsCodeFixProvider.cs deleted file mode 100644 index 773fe62a663dd..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; - -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForAccessorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, - FeaturesResources.Use_expression_body_for_accessors, - FeaturesResources.Use_block_body_for_accessors) - { - } - - protected override SyntaxToken GetSemicolonToken(AccessorDeclarationSyntax declaration) - => declaration.SemicolonToken; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) - => declaration.ExpressionBody; - - protected override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) - => declaration.Body; - - protected override AccessorDeclarationSyntax WithSemicolonToken(AccessorDeclarationSyntax declaration, SyntaxToken token) - => declaration.WithSemicolonToken(token); - - protected override AccessorDeclarationSyntax WithExpressionBody(AccessorDeclarationSyntax declaration, ArrowExpressionClauseSyntax expressionBody) - => declaration.WithExpressionBody(expressionBody); - - protected override AccessorDeclarationSyntax WithBody(AccessorDeclarationSyntax declaration, BlockSyntax body) - => declaration.WithBody(body); - - protected override bool CreateReturnStatementForExpression(AccessorDeclarationSyntax declaration) - => declaration.IsKind(SyntaxKind.GetAccessorDeclaration); - } -} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs deleted file mode 100644 index ab61cb708e8bb..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Accessors/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,61 +0,0 @@ -// 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 Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; - -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); - private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); - - public UseExpressionBodyForAccessorsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedAccessors) - { - } - - protected override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) - => declaration.Body; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) - => declaration.ExpressionBody; - - internal override Diagnostic AnalyzeSyntax(OptionSet optionSet, AccessorDeclarationSyntax accessor) - { - // We don't want to double report. So don't report a diagnostic if the property/indexer - // analyzer is going to report a diagnostic here. - var grandParent = accessor.Parent.Parent; - - if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) - { - var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; - var diagnostic = propertyAnalyzer.AnalyzeSyntax(optionSet, propertyDeclaration); - if (diagnostic != null && diagnostic.Severity != DiagnosticSeverity.Hidden) - { - return null; - } - } - else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) - { - var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; - var diagnostic = indexerAnalyzer.AnalyzeSyntax(optionSet, indexerDeclaration); - if (diagnostic != null && diagnostic.Severity != DiagnosticSeverity.Hidden) - { - return null; - } - } - - return base.AnalyzeSyntax(optionSet, accessor); - } - } -} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..d9e72f43d7e9d --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs @@ -0,0 +1,93 @@ +// 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.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + internal abstract class AbstractUseExpressionBodyDiagnosticAnalyzer : + AbstractCodeStyleDiagnosticAnalyzer + where TDeclaration : SyntaxNode + { + private readonly ImmutableArray _syntaxKinds; + + public override bool OpenFileOnly(Workspace workspace) => false; + + private readonly AbstractUseExpressionBodyHelper _helper; + + protected AbstractUseExpressionBodyDiagnosticAnalyzer( + string diagnosticId, + ImmutableArray syntaxKinds, + AbstractUseExpressionBodyHelper helper) + : base(diagnosticId, helper.UseExpressionBodyTitle) + { + _syntaxKinds = syntaxKinds; + _helper = helper; + } + + public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; + + protected override void InitializeWorker(AnalysisContext context) + => context.RegisterSyntaxNodeAction(AnalyzeSyntax, _syntaxKinds); + + private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) + { + var options = context.Options; + var syntaxTree = context.Node.SyntaxTree; + var cancellationToken = context.CancellationToken; + var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult(); + if (optionSet == null) + { + return; + } + + var diagnostic = AnalyzeSyntax(optionSet, (TDeclaration)context.Node); + if (diagnostic != null) + { + context.ReportDiagnostic(diagnostic); + } + } + + private Diagnostic AnalyzeSyntax(OptionSet optionSet, TDeclaration declaration) + { + var preferExpressionBodiedOption = optionSet.GetOption(_helper.Option); + var severity = preferExpressionBodiedOption.Notification.Value; + + if (_helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: true)) + { + var location = severity == DiagnosticSeverity.Hidden + ? declaration.GetLocation() + : _helper.GetBody(declaration).Statements[0].GetLocation(); + + var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); + return Diagnostic.Create( + CreateDescriptorWithTitle(_helper.UseExpressionBodyTitle, severity, GetCustomTags(severity)), + location, additionalLocations: additionalLocations); + } + + if (_helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: true)) + { + // They have an expression body. Create a diagnostic to conver it to a block + // if they don't want expression bodies for this member. + var location = severity == DiagnosticSeverity.Hidden + ? declaration.GetLocation() + : _helper.GetExpressionBody(declaration).GetLocation(); + + var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); + return Diagnostic.Create( + CreateDescriptorWithTitle(_helper.UseBlockBodyTitle, severity, GetCustomTags(severity)), + location, additionalLocations: additionalLocations); + } + + return null; + } + + private static string[] GetCustomTags(DiagnosticSeverity severity) + => severity == DiagnosticSeverity.Hidden + ? new[] { WellKnownDiagnosticTags.NotConfigurable } + : Array.Empty(); + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs new file mode 100644 index 0000000000000..4fbde52a1aaa8 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs @@ -0,0 +1,23 @@ +// 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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : + AbstractUseExpressionBodyDiagnosticAnalyzer + { + private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); + private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); + + public UseExpressionBodyForAccessorsDiagnosticAnalyzer() + : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, + ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration), + new UseExpressionBodyForAccessorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs similarity index 51% rename from src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs index 4c4164e556491..ed714425277b6 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : { public UseExpressionBodyForConstructorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.ConstructorDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedConstructors) + new UseExpressionBodyForConstructorsHelper()) { } - - protected override BlockSyntax GetBody(ConstructorDeclarationSyntax declaration) - => declaration.Body; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(ConstructorDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs similarity index 52% rename from src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs index 5f5201ba5c787..ffc8ea4142820 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : { public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.ConversionOperatorDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedOperators) + new UseExpressionBodyForConversionOperatorsHelper()) { } - - protected override BlockSyntax GetBody(ConversionOperatorDeclarationSyntax declaration) - => declaration.Body; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(ConversionOperatorDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs similarity index 50% rename from src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs index d364161cd46bd..c799acf60f807 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForIndexersDiagnosticAnalyzer : { public UseExpressionBodyForIndexersDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.IndexerDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedIndexers) + UseExpressionBodyForIndexersHelper.Instance) { } - - protected override BlockSyntax GetBody(IndexerDeclarationSyntax declaration) - => GetBodyFromSingleGetAccessor(declaration.AccessorList); - - protected override ArrowExpressionClauseSyntax GetExpressionBody(IndexerDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs similarity index 51% rename from src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs index 9acf7f481a05d..31607db8b5b13 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : { public UseExpressionBodyForMethodsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.MethodDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedMethods) + new UseExpressionBodyForMethodsHelper()) { } - - protected override BlockSyntax GetBody(MethodDeclarationSyntax declaration) - => declaration.Body; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(MethodDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs similarity index 51% rename from src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs index 70ff159322588..b587575312124 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : { public UseExpressionBodyForOperatorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.OperatorDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedOperators) + new UseExpressionBodyForOperatorsHelper()) { } - - protected override BlockSyntax GetBody(OperatorDeclarationSyntax declaration) - => declaration.Body; - - protected override ArrowExpressionClauseSyntax GetExpressionBody(OperatorDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs similarity index 50% rename from src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs index e5471c390e8ba..b2e98395f9ae6 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs @@ -1,7 +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.Immutable; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; @@ -13,17 +12,9 @@ internal class UseExpressionBodyForPropertiesDiagnosticAnalyzer : { public UseExpressionBodyForPropertiesDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, - new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), ImmutableArray.Create(SyntaxKind.PropertyDeclaration), - CSharpCodeStyleOptions.PreferExpressionBodiedProperties) + UseExpressionBodyForPropertiesHelper.Instance) { } - - protected override BlockSyntax GetBody(PropertyDeclarationSyntax declaration) - => GetBodyFromSingleGetAccessor(declaration.AccessorList); - - protected override ArrowExpressionClauseSyntax GetExpressionBody(PropertyDeclarationSyntax declaration) - => declaration.ExpressionBody; } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs new file mode 100644 index 0000000000000..aa15476949d1a --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs @@ -0,0 +1,88 @@ +// 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.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + internal abstract partial class AbstractUseExpressionBodyCodeFixProvider : + SyntaxEditorBasedCodeFixProvider + where TDeclaration : SyntaxNode + { + private readonly AbstractUseExpressionBodyHelper _helper; + + public sealed override ImmutableArray FixableDiagnosticIds { get; } + + protected AbstractUseExpressionBodyCodeFixProvider( + string diagnosticId, + AbstractUseExpressionBodyHelper helper) + { + FixableDiagnosticIds = ImmutableArray.Create(diagnosticId); + _helper = helper; + } + + protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic) + => diagnostic.Severity != DiagnosticSeverity.Hidden; + + public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var diagnostic = context.Diagnostics.First(); + var documentOptionSet = await context.Document.GetOptionsAsync(context.CancellationToken).ConfigureAwait(false); + + var priority = diagnostic.Severity == DiagnosticSeverity.Hidden + ? CodeActionPriority.Low + : CodeActionPriority.Medium; + + context.RegisterCodeFix( + new MyCodeAction(diagnostic.GetMessage(), priority, c => FixAsync(context.Document, diagnostic, c)), + diagnostic); + } + + protected override async Task FixAllAsync( + Document document, ImmutableArray diagnostics, + SyntaxEditor editor, CancellationToken cancellationToken) + { + var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + + foreach (var diagnostic in diagnostics) + { + cancellationToken.ThrowIfCancellationRequested(); + AddEdits(editor, diagnostic, options, cancellationToken); + } + } + + private void AddEdits( + SyntaxEditor editor, Diagnostic diagnostic, + OptionSet options, CancellationToken cancellationToken) + { + var declarationLocation = diagnostic.AdditionalLocations[0]; + var declaration = (TDeclaration)declarationLocation.FindNode(cancellationToken); + + var updatedDeclaration = _helper.Update(declaration, options) + .WithAdditionalAnnotations(Formatter.Annotation); + + editor.ReplaceNode(declaration, updatedDeclaration); + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + internal override CodeActionPriority Priority { get; } + + public MyCodeAction(string title, CodeActionPriority priority, Func> createChangedDocument) + : base(title, createChangedDocument) + { + this.Priority = priority; + } + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs new file mode 100644 index 0000000000000..7fa4433561247 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs @@ -0,0 +1,20 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForAccessorsCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, + new UseExpressionBodyForAccessorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs new file mode 100644 index 0000000000000..dd3e31908adb9 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs @@ -0,0 +1,19 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForConstructorsCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, + new UseExpressionBodyForConstructorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs new file mode 100644 index 0000000000000..2e54838852fb8 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs @@ -0,0 +1,19 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForConversionOperatorsCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, + new UseExpressionBodyForConversionOperatorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs new file mode 100644 index 0000000000000..57023616eb940 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs @@ -0,0 +1,22 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForIndexersCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, + UseExpressionBodyForIndexersHelper.Instance) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs new file mode 100644 index 0000000000000..e1341d72b7dda --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs @@ -0,0 +1,19 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForMethodsCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, + new UseExpressionBodyForMethodsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs new file mode 100644 index 0000000000000..d5d4ccd0cbbe2 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs @@ -0,0 +1,19 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForOperatorsCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, + new UseExpressionBodyForOperatorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs new file mode 100644 index 0000000000000..bd40019aaeda2 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs @@ -0,0 +1,19 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + { + public UseExpressionBodyForPropertiesCodeFixProvider() + : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, + UseExpressionBodyForPropertiesHelper.Instance) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..481ef4bac751f --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs @@ -0,0 +1,99 @@ +// 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.Immutable; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Extensions; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + internal abstract class AbstractUseExpressionBodyCodeRefactoringProvider : + CodeRefactoringProvider + where TDeclaration : SyntaxNode + { + private readonly AbstractUseExpressionBodyHelper _helper; + + protected AbstractUseExpressionBodyCodeRefactoringProvider( + AbstractUseExpressionBodyHelper helper) + { + _helper = helper; + } + + public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) + { + if (context.Span.Length > 0) + { + return; + } + + var position = context.Span.Start; + var document = context.Document; + var cancellationToken = context.CancellationToken; + + var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var node = root.FindToken(position).Parent; + if (node == null) + { + return; + } + + var containingLambda = node.FirstAncestorOrSelf(); + if (containingLambda != null && + node.AncestorsAndSelf().Contains(containingLambda.Body)) + { + // don't offer inside a lambda. Lambdas can be quite large, and it will be very noisy + // inside the body of one to be offering to use a block/expression body for the containing + // class member. + return; + } + + var declaration = node.FirstAncestorOrSelf(); + if (declaration == null) + { + return; + } + + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + + if (_helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: false)) + { + context.RegisterRefactoring(new MyCodeAction( + _helper.UseExpressionBodyTitle.ToString(), + c => UpdateDocumentAsync(document, root, declaration, optionSet, c))); + } + + if (_helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: false)) + { + context.RegisterRefactoring(new MyCodeAction( + _helper.UseBlockBodyTitle.ToString(), + c => UpdateDocumentAsync(document, root, declaration, optionSet, c))); + } + } + + private Task UpdateDocumentAsync( + Document document, SyntaxNode root, TDeclaration declaration, + DocumentOptionSet options, CancellationToken cancellationToken) + { + var updatedDeclaration = _helper.Update(declaration, options) + .WithAdditionalAnnotations(Formatter.Annotation); + var newRoot = root.ReplaceNode(declaration, updatedDeclaration); + + return Task.FromResult(document.WithSyntaxRoot(newRoot)); + } + + private class MyCodeAction : CodeAction.DocumentChangeAction + { + public MyCodeAction(string title, Func> createChangedDocument) + : base(title, createChangedDocument) + { + } + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..b2b180fd6cb85 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForAccessorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForAccessorsCodeRefactoringProvider() + : base(new UseExpressionBodyForAccessorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..8269679de373e --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForConstructorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForConstructorsCodeRefactoringProvider() + : base(new UseExpressionBodyForConstructorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..378498f438b12 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForConversionOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForConversionOperatorsCodeRefactoringProvider() + : base(new UseExpressionBodyForConversionOperatorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..1e8a031124541 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForIndexersCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForIndexersCodeRefactoringProvider() + : base(UseExpressionBodyForIndexersHelper.Instance) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..f48454c88d545 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForMethodsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForMethodsCodeRefactoringProvider() + : base(new UseExpressionBodyForMethodsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..1b648dae07d08 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForOperatorsCodeRefactoringProvider() + : base(new UseExpressionBodyForOperatorsHelper()) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs new file mode 100644 index 0000000000000..041fcebf2ae67 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs @@ -0,0 +1,17 @@ +// 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.Composition; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyForPropertiesCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider + { + public UseExpressionBodyForPropertiesCodeRefactoringProvider() + : base(UseExpressionBodyForPropertiesHelper.Instance) + { + } + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs similarity index 54% rename from src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs index f001d140d9cb6..86a1a7be6a934 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/AbstractUseExpressionBodyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs @@ -1,90 +1,102 @@ // 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.Immutable; using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeActions; -using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Editing; -using Microsoft.CodeAnalysis.Formatting; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - internal abstract partial class AbstractUseExpressionBodyCodeFixProvider : - SyntaxEditorBasedCodeFixProvider + /// + /// Helper class that allows us to share lots of logic between the diagnostic analyzer and the + /// code refactoring provider. Those can't share a common base class due to their own inheritance + /// requirements with and . + /// + internal abstract class AbstractUseExpressionBodyHelper where TDeclaration : SyntaxNode { - private readonly Option> _option; - private readonly string _useExpressionBodyTitle; - private readonly string _useBlockBodyTitle; - - public sealed override ImmutableArray FixableDiagnosticIds { get; } - - protected AbstractUseExpressionBodyCodeFixProvider( - string diagnosticId, - Option> option, - string useExpressionBodyTitle, - string useBlockBodyTitle) + public readonly Option> Option; + public readonly LocalizableString UseExpressionBodyTitle; + public readonly LocalizableString UseBlockBodyTitle; + + protected AbstractUseExpressionBodyHelper( + LocalizableString useExpressionBodyTitle, + LocalizableString useBlockBodyTitle, + Option> option) { - FixableDiagnosticIds = ImmutableArray.Create(diagnosticId); - _option = option; - _useExpressionBodyTitle = useExpressionBodyTitle; - _useBlockBodyTitle = useBlockBodyTitle; + Option = option; + UseExpressionBodyTitle = useExpressionBodyTitle; + UseBlockBodyTitle = useBlockBodyTitle; } - protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic) - => diagnostic.Severity != DiagnosticSeverity.Hidden; + public abstract BlockSyntax GetBody(TDeclaration declaration); + public abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration); - public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) + protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList) { - var diagnostic = context.Diagnostics.First(); - var documentOptionSet = await context.Document.GetOptionsAsync(context.CancellationToken).ConfigureAwait(false); - - var priority = diagnostic.Severity == DiagnosticSeverity.Hidden - ? CodeActionPriority.Low - : CodeActionPriority.Medium; + if (accessorList != null && + accessorList.Accessors.Count == 1 && + accessorList.Accessors[0].AttributeLists.Count == 0 && + accessorList.Accessors[0].IsKind(SyntaxKind.GetAccessorDeclaration)) + { + return accessorList.Accessors[0].Body; + } - context.RegisterCodeFix( - new MyCodeAction(diagnostic.GetMessage(), priority, c => FixAsync(context.Document, diagnostic, c)), - diagnostic); + return null; } - protected override async Task FixAllAsync( - Document document, ImmutableArray diagnostics, - SyntaxEditor editor, CancellationToken cancellationToken) + public virtual bool CanOfferUseExpressionBody( + OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { - var options = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var preference = optionSet.GetOption(this.Option).Value; + var userPrefersExpressionBodies = preference != ExpressionBodyPreference.Never; - foreach (var diagnostic in diagnostics) + // If the user likes expression bodies, then we offer expression bodies from the diagnostic analyzer. + // If the user does not like expression bodies then we offer expression bodies from the refactoring provider. + if (userPrefersExpressionBodies == forAnalyzer) { - cancellationToken.ThrowIfCancellationRequested(); - AddEdits(editor, diagnostic, options, cancellationToken); + var expressionBody = this.GetExpressionBody(declaration); + if (expressionBody == null) + { + // They don't have an expression body. See if we could convert the block they + // have into one. + + var options = declaration.SyntaxTree.Options; + var body = this.GetBody(declaration); + if (body.TryConvertToExpressionBody(options, preference, + out var expressionWhenOnSingleLine, out var semicolonWhenOnSingleLine)) + { + return true; + } + } } + + return false; } - private void AddEdits( - SyntaxEditor editor, Diagnostic diagnostic, - OptionSet options, CancellationToken cancellationToken) + public virtual bool CanOfferUseBlockBody( + OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { - var declarationLocation = diagnostic.AdditionalLocations[0]; - var declaration = (TDeclaration)declarationLocation.FindNode(cancellationToken); + var preference = optionSet.GetOption(this.Option).Value; + var userPrefersBlockBodies = preference == ExpressionBodyPreference.Never; - var updatedDeclaration = this.Update(declaration, options) - .WithAdditionalAnnotations(Formatter.Annotation); + // If the user likes block bodies, then we offer block bodies from the diagnostic analyzer. + // If the user does not like block bodies then we offer block bodies from the refactoring provider. + if (userPrefersBlockBodies == forAnalyzer) + { + // If we have an expression body, we can always convert it to a block body. + return this.GetExpressionBody(declaration) != null; + } - editor.ReplaceNode(declaration, updatedDeclaration); + return false; } - private TDeclaration Update(TDeclaration declaration, OptionSet options) + public TDeclaration Update(TDeclaration declaration, OptionSet options) { var preferExpressionBody = GetBody(declaration) != null; if (preferExpressionBody) @@ -116,8 +128,6 @@ private TDeclaration Update(TDeclaration declaration, OptionSet options) protected abstract bool CreateReturnStatementForExpression(TDeclaration declaration); protected abstract SyntaxToken GetSemicolonToken(TDeclaration declaration); - protected abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration); - protected abstract BlockSyntax GetBody(TDeclaration declaration); protected abstract TDeclaration WithSemicolonToken(TDeclaration declaration, SyntaxToken token); protected abstract TDeclaration WithExpressionBody(TDeclaration declaration, ArrowExpressionClauseSyntax expressionBody); @@ -166,16 +176,5 @@ protected virtual TDeclaration WithAccessorList(TDeclaration declaration, Access { throw new NotImplementedException(); } - - private class MyCodeAction : CodeAction.DocumentChangeAction - { - internal override CodeActionPriority Priority { get; } - - public MyCodeAction(string title, CodeActionPriority priority, Func> createChangedDocument) - : base(title, createChangedDocument) - { - this.Priority = priority; - } - } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs new file mode 100644 index 0000000000000..0b998665f89cc --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -0,0 +1,100 @@ +// 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 Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + internal class UseExpressionBodyForAccessorsHelper : + AbstractUseExpressionBodyHelper + { + private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); + private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); + + public UseExpressionBodyForAccessorsHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedAccessors) + { + } + + public override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) + => declaration.Body; + + public override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) + => declaration.ExpressionBody; + + public override bool CanOfferUseExpressionBody( + OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + { + var grandParent = accessor.Parent.Parent; + + if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) + { + var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; + if (UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody( + optionSet, propertyDeclaration, forAnalyzer)) + { + return false; + } + } + else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) + { + var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; + if (UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody( + optionSet, indexerDeclaration, forAnalyzer)) + { + return false; + } + } + + return base.CanOfferUseExpressionBody(optionSet, accessor, forAnalyzer); + } + + public override bool CanOfferUseBlockBody( + OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + { + var grandParent = accessor.Parent.Parent; + + if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) + { + var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; + if (UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody( + optionSet, propertyDeclaration, forAnalyzer)) + { + return false; + } + } + else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) + { + var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; + if (UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody( + optionSet, indexerDeclaration, forAnalyzer)) + { + return false; + } + } + + return base.CanOfferUseBlockBody(optionSet, accessor, forAnalyzer); + } + + protected override SyntaxToken GetSemicolonToken(AccessorDeclarationSyntax declaration) + => declaration.SemicolonToken; + + protected override AccessorDeclarationSyntax WithSemicolonToken(AccessorDeclarationSyntax declaration, SyntaxToken token) + => declaration.WithSemicolonToken(token); + + protected override AccessorDeclarationSyntax WithExpressionBody(AccessorDeclarationSyntax declaration, ArrowExpressionClauseSyntax expressionBody) + => declaration.WithExpressionBody(expressionBody); + + protected override AccessorDeclarationSyntax WithBody(AccessorDeclarationSyntax declaration, BlockSyntax body) + => declaration.WithBody(body); + + protected override bool CreateReturnStatementForExpression(AccessorDeclarationSyntax declaration) + => declaration.IsKind(SyntaxKind.GetAccessorDeclaration); + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs similarity index 62% rename from src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index e6a68134537c8..65dcd12f4489f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Constructors/UseExpressionBodyForConstructorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -1,32 +1,28 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForConstructorsHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForConstructorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, - FeaturesResources.Use_expression_body_for_constructors, - FeaturesResources.Use_block_body_for_constructors) + public UseExpressionBodyForConstructorsHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedConstructors) { } - protected override SyntaxToken GetSemicolonToken(ConstructorDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(ConstructorDeclarationSyntax declaration) + => declaration.Body; - protected override ArrowExpressionClauseSyntax GetExpressionBody(ConstructorDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(ConstructorDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(ConstructorDeclarationSyntax declaration) - => declaration.Body; + protected override SyntaxToken GetSemicolonToken(ConstructorDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override ConstructorDeclarationSyntax WithSemicolonToken(ConstructorDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs similarity index 62% rename from src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index 5ce5e92d575c6..2a6b2eeaa4068 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -1,32 +1,28 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForConversionOperatorsHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForConversionOperatorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedOperators, - FeaturesResources.Use_expression_body_for_operators, - FeaturesResources.Use_block_body_for_operators) + public UseExpressionBodyForConversionOperatorsHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedOperators) { } - protected override SyntaxToken GetSemicolonToken(ConversionOperatorDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(ConversionOperatorDeclarationSyntax declaration) + => declaration.Body; - protected override ArrowExpressionClauseSyntax GetExpressionBody(ConversionOperatorDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(ConversionOperatorDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(ConversionOperatorDeclarationSyntax declaration) - => declaration.Body; + protected override SyntaxToken GetSemicolonToken(ConversionOperatorDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override ConversionOperatorDeclarationSyntax WithSemicolonToken(ConversionOperatorDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); @@ -40,5 +36,4 @@ protected override ConversionOperatorDeclarationSyntax WithBody(ConversionOperat protected override bool CreateReturnStatementForExpression(ConversionOperatorDeclarationSyntax declaration) => true; } - } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs similarity index 67% rename from src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs index 310f7b1af3a00..88736c927fdad 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Indexers/UseExpressionBodyForIndexersCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs @@ -1,34 +1,32 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForIndexersHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForIndexersCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, - FeaturesResources.Use_expression_body_for_indexers, - FeaturesResources.Use_block_body_for_indexers) + public static readonly UseExpressionBodyForIndexersHelper Instance = new UseExpressionBodyForIndexersHelper(); + + private UseExpressionBodyForIndexersHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedIndexers) { } - protected override SyntaxToken GetSemicolonToken(IndexerDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(IndexerDeclarationSyntax declaration) + => GetBodyFromSingleGetAccessor(declaration.AccessorList); - protected override ArrowExpressionClauseSyntax GetExpressionBody(IndexerDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(IndexerDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(IndexerDeclarationSyntax declaration) - => declaration.AccessorList?.Accessors[0].Body; + protected override SyntaxToken GetSemicolonToken(IndexerDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override IndexerDeclarationSyntax WithSemicolonToken(IndexerDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs similarity index 64% rename from src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index 34383d537ce6d..35ebf348877b5 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Methods/UseExpressionBodyForMethodsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -1,33 +1,29 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForMethodsHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForMethodsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedMethods, - FeaturesResources.Use_expression_body_for_methods, - FeaturesResources.Use_block_body_for_methods) + public UseExpressionBodyForMethodsHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedMethods) { } - protected override SyntaxToken GetSemicolonToken(MethodDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(MethodDeclarationSyntax declaration) + => declaration.Body; - protected override ArrowExpressionClauseSyntax GetExpressionBody(MethodDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(MethodDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(MethodDeclarationSyntax declaration) - => declaration.Body; + protected override SyntaxToken GetSemicolonToken(MethodDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override MethodDeclarationSyntax WithSemicolonToken(MethodDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); @@ -41,5 +37,4 @@ protected override MethodDeclarationSyntax WithBody(MethodDeclarationSyntax decl protected override bool CreateReturnStatementForExpression(MethodDeclarationSyntax declaration) => !declaration.ReturnType.IsVoid(); } - } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs similarity index 62% rename from src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index 752365bcda82c..2b1af26313652 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Operators/UseExpressionBodyForOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -1,32 +1,28 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForOperatorsHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForOperatorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedOperators, - FeaturesResources.Use_expression_body_for_operators, - FeaturesResources.Use_block_body_for_operators) + public UseExpressionBodyForOperatorsHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedOperators) { } - protected override SyntaxToken GetSemicolonToken(OperatorDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(OperatorDeclarationSyntax declaration) + => declaration.Body; - protected override ArrowExpressionClauseSyntax GetExpressionBody(OperatorDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(OperatorDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(OperatorDeclarationSyntax declaration) - => declaration.Body; + protected override SyntaxToken GetSemicolonToken(OperatorDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override OperatorDeclarationSyntax WithSemicolonToken(OperatorDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); @@ -40,5 +36,4 @@ protected override OperatorDeclarationSyntax WithBody(OperatorDeclarationSyntax protected override bool CreateReturnStatementForExpression(OperatorDeclarationSyntax declaration) => true; } - } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs similarity index 67% rename from src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 2cc4621a4c22a..199e4ee9c2a3c 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Properties/UseExpressionBodyForPropertiesCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -1,36 +1,32 @@ // 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider + internal class UseExpressionBodyForPropertiesHelper : + AbstractUseExpressionBodyHelper { - public UseExpressionBodyForPropertiesCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, - CSharpCodeStyleOptions.PreferExpressionBodiedProperties, - FeaturesResources.Use_expression_body_for_properties, - FeaturesResources.Use_block_body_for_properties) + public static readonly UseExpressionBodyForPropertiesHelper Instance = new UseExpressionBodyForPropertiesHelper(); + + private UseExpressionBodyForPropertiesHelper() + : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + CSharpCodeStyleOptions.PreferExpressionBodiedProperties) { } - protected override SyntaxToken GetSemicolonToken(PropertyDeclarationSyntax declaration) - => declaration.SemicolonToken; + public override BlockSyntax GetBody(PropertyDeclarationSyntax declaration) + => GetBodyFromSingleGetAccessor(declaration.AccessorList); - protected override ArrowExpressionClauseSyntax GetExpressionBody(PropertyDeclarationSyntax declaration) + public override ArrowExpressionClauseSyntax GetExpressionBody(PropertyDeclarationSyntax declaration) => declaration.ExpressionBody; - protected override BlockSyntax GetBody(PropertyDeclarationSyntax declaration) - => declaration.AccessorList?.Accessors[0].Body; + protected override SyntaxToken GetSemicolonToken(PropertyDeclarationSyntax declaration) + => declaration.SemicolonToken; protected override PropertyDeclarationSyntax WithSemicolonToken(PropertyDeclarationSyntax declaration, SyntaxToken token) => declaration.WithSemicolonToken(token); From 93458352a5c88ee66e2d154cbe2672462b119cc2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:29:01 -0700 Subject: [PATCH 018/214] Update tests. --- .../UseExpressionBodyForAccessorsTests.cs | 21 ++----------------- .../AbstractUseExpressionBodyHelper.cs | 5 ++++- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs index 1e33d62be4ee3..14d4a31715653 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs @@ -140,31 +140,14 @@ int Foo [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] public async Task TestMissingWithOnlySetter() { - await TestActionCountAsync( + await TestMissingAsync( @"class C { int Foo { set => [|Bar|](); } -}", count: 1, parameters: new TestParameters(options: UseExpressionBody)); - - // There is a hidden diagnostic that still offers to convert expression-body to block-body. - await TestInRegularAndScriptAsync( -@"class C -{ - int Foo - { - set => [|Bar|](); - } -}", -@"class C -{ - int Foo - { - set { Bar(); } - } -}", options: UseExpressionBody); +}"); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs index 86a1a7be6a934..0ef6f125eab4d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs @@ -68,7 +68,10 @@ public virtual bool CanOfferUseExpressionBody( var options = declaration.SyntaxTree.Options; var body = this.GetBody(declaration); - if (body.TryConvertToExpressionBody(options, preference, + + var conversionPreference = forAnalyzer ? preference : ExpressionBodyPreference.WhenPossible; + + if (body.TryConvertToExpressionBody(options, conversionPreference, out var expressionWhenOnSingleLine, out var semicolonWhenOnSingleLine)) { return true; From 506a455d8e8d827a913d8f54fb3b41ac44332437 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:31:00 -0700 Subject: [PATCH 019/214] Rename tests. --- .../CSharpTest/CSharpEditorServicesTest.csproj | 14 +++++++------- ... UseExpressionBodyForAccessorsAnalyzerTests.cs} | 0 ...eExpressionBodyForConstructorsAnalyzerTests.cs} | 2 +- ...sionBodyForConversionOperatorsAnalyzerTests.cs} | 2 +- ...> UseExpressionBodyForIndexersAnalyzerTests.cs} | 2 +- ...=> UseExpressionBodyForMethodsAnalyzerTests.cs} | 2 +- ... UseExpressionBodyForOperatorsAnalyzerTests.cs} | 2 +- ...UseExpressionBodyForPropertiesAnalyzerTests.cs} | 2 +- 8 files changed, 13 insertions(+), 13 deletions(-) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForAccessorsTests.cs => UseExpressionBodyForAccessorsAnalyzerTests.cs} (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForConstructorsTests.cs => UseExpressionBodyForConstructorsAnalyzerTests.cs} (97%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForConversionOperatorsTests.cs => UseExpressionBodyForConversionOperatorsAnalyzerTests.cs} (97%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForIndexerTests.cs => UseExpressionBodyForIndexersAnalyzerTests.cs} (98%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForMethodsTests.cs => UseExpressionBodyForMethodsAnalyzerTests.cs} (98%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForOperatorsTests.cs => UseExpressionBodyForOperatorsAnalyzerTests.cs} (97%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{UseExpressionBodyForPropertiesTests.cs => UseExpressionBodyForPropertiesAnalyzerTests.cs} (98%) diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index fb163f46e950a..0548ccf208c4e 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -395,13 +395,13 @@ - - - - - - - + + + + + + + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs similarity index 97% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs index 179293cba2105..45c0ce53b6765 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForConstructorsTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForConstructorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForConstructorsDiagnosticAnalyzer(), diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs similarity index 97% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs index e3096b8f56537..fd9065976eeeb 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForConversionOperatorsTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForConversionOperatorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer(), diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexersAnalyzerTests.cs similarity index 98% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexersAnalyzerTests.cs index 99ed686376cdf..944961d0d45d5 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexersAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForIndexersTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForIndexersAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForIndexersDiagnosticAnalyzer(), new UseExpressionBodyForIndexersCodeFixProvider()); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs similarity index 98% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs index 8d07ffd193e07..780cdc1ffb8e3 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForMethodsTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForMethodsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForMethodsDiagnosticAnalyzer(), new UseExpressionBodyForMethodsCodeFixProvider()); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsAnalyzerTests.cs similarity index 97% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsAnalyzerTests.cs index 93e0e72e657b5..6fc8e0842844c 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForOperatorsTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForOperatorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForOperatorsDiagnosticAnalyzer(), diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesAnalyzerTests.cs similarity index 98% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesAnalyzerTests.cs index c6b72054ffbfb..a904ff63bd61b 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesAnalyzerTests.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody { - public class UseExpressionBodyForPropertiesTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + public class UseExpressionBodyForPropertiesAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) => (new UseExpressionBodyForPropertiesDiagnosticAnalyzer(), new UseExpressionBodyForPropertiesCodeFixProvider()); From 29e1d575d7f26f9097104b9f079841b32281ca48 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:40:13 -0700 Subject: [PATCH 020/214] Add tests. --- .../CSharpEditorServicesTest.csproj | 1 + ...ressionBodyForMethodsRefafactoringTests.cs | 98 +++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 0548ccf208c4e..093087552a990 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -360,6 +360,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs new file mode 100644 index 0000000000000..a615d356ae425 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs @@ -0,0 +1,98 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; +using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForMethodsRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForMethodsCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + void Foo() + { + [||]Bar(); + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + void Foo() + { + [||]Bar(); + } +}", +@"class C +{ + void Foo() => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + Action Foo() + { + return () => { [||] }; + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + void Foo() => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + void Foo() => [||]Bar(); +}", +@"class C +{ + void Foo() { Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From 7af89211255df1f575f95aad83f3ee8968636aeb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:40:52 -0700 Subject: [PATCH 021/214] Move tests. --- .../{ => Analyzer}/UseExpressionBodyForAccessorsAnalyzerTests.cs | 0 .../UseExpressionBodyForConstructorsAnalyzerTests.cs | 0 .../UseExpressionBodyForConversionOperatorsAnalyzerTests.cs | 0 .../{ => Analyzer}/UseExpressionBodyForIndexersAnalyzerTests.cs | 0 .../{ => Analyzer}/UseExpressionBodyForMethodsAnalyzerTests.cs | 0 .../{ => Analyzer}/UseExpressionBodyForOperatorsAnalyzerTests.cs | 0 .../{ => Analyzer}/UseExpressionBodyForPropertiesAnalyzerTests.cs | 0 .../UseExpressionBodyForMethodsRefafactoringTests.cs | 0 8 files changed, 0 insertions(+), 0 deletions(-) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForAccessorsAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForConstructorsAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForIndexersAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForMethodsAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForOperatorsAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Analyzer}/UseExpressionBodyForPropertiesAnalyzerTests.cs (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/{ => Refactoring}/UseExpressionBodyForMethodsRefafactoringTests.cs (100%) diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForAccessorsAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConstructorsAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexersAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForIndexersAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForOperatorsAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForPropertiesAnalyzerTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/UseExpressionBodyForMethodsRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs From 684ef24de15d792594489e9bce5a26db6d88606c Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:45:04 -0700 Subject: [PATCH 022/214] Add tests for operators. --- .../CSharpEditorServicesTest.csproj | 17 ++-- ...ressionBodyForMethodsRefafactoringTests.cs | 5 - ...ssionBodyForOperatorsRefafactoringTests.cs | 93 +++++++++++++++++++ 3 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 093087552a990..ffdcd31325d29 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -360,7 +360,8 @@ - + + @@ -396,13 +397,13 @@ - - - - - - - + + + + + + + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs index a615d356ae425..acaa144327a78 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs @@ -2,15 +2,10 @@ using System.Collections.Generic; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics; -using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions; using Microsoft.CodeAnalysis.Options; using Roslyn.Test.Utilities; using Xunit; diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs new file mode 100644 index 0000000000000..41287510c9826 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs @@ -0,0 +1,93 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForOperatorsRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForOperatorsCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + public static bool operator +(C c1, C c2) + { + [||]Bar(); + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public static bool operator +(C c1, C c2) + { + [||]Bar(); + } +}", +@"class C +{ + public static bool operator +(C c1, C c2) => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + public static bool operator +(C c1, C c2) + { + return () => { [||] }; + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + public static bool operator +(C c1, C c2) => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public static bool operator +(C c1, C c2) => [||]Bar(); +}", +@"class C +{ + public static bool operator +(C c1, C c2) { return Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From c27e9c010fae0db203f8e43383b4b603d67d4625 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:48:10 -0700 Subject: [PATCH 023/214] Add tests for constructors. --- .../CSharpEditorServicesTest.csproj | 1 + ...onBodyForConstructorsRefafactoringTests.cs | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index ffdcd31325d29..d1228b9ff0628 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -360,6 +360,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs new file mode 100644 index 0000000000000..98ffe015b69f1 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs @@ -0,0 +1,93 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForConstructorsRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForConstructorsCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + public C() + { + [||]Bar(); + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public C() + { + [||]Bar(); + } +}", +@"class C +{ + public C() => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + public C() + { + return () => { [||] }; + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + public C() => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public C() => [||]Bar(); +}", +@"class C +{ + public C() { Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From 0ba570fea5c5fcbb4d1147ca88d5f396abbada7d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:50:11 -0700 Subject: [PATCH 024/214] Add tests for constructors. --- .../CSharpEditorServicesTest.csproj | 1 + ...orConversionOperatorsRefafactoringTests.cs | 93 +++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index d1228b9ff0628..fd9fcebfa6746 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -361,6 +361,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs new file mode 100644 index 0000000000000..5604e61c426bd --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs @@ -0,0 +1,93 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForConversionOperatorsRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForConversionOperatorsCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + public static implicit operator bool(C c1) + { + [||]Bar(); + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public static implicit operator bool(C c1) + { + [||]Bar(); + } +}", +@"class C +{ + public static implicit operator bool(C c1) => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + public static implicit operator bool(C c1) + { + return () => { [||] }; + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + public static implicit operator bool(C c1) => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + public static implicit operator bool(C c1) => [||]Bar(); +}", +@"class C +{ + public static implicit operator bool(C c1) { return Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From 3b79dc85b236b458baf4da2c90b1c3e54df8afcf Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:54:51 -0700 Subject: [PATCH 025/214] Add tests for properties. --- .../CSharpEditorServicesTest.csproj | 1 + ...sionBodyForPropertiesRefafactoringTests.cs | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index fd9fcebfa6746..cfe41f9a0cbc3 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -362,6 +362,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs new file mode 100644 index 0000000000000..69209fe76555b --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs @@ -0,0 +1,102 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForPropertiesRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForPropertiesCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", +@"class C +{ + int Foo => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + Action Foo + { + get + { + return () => { [||] }; + } + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + int Foo => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo => [||]Bar(); +}", +@"class C +{ + int Foo { get => Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From ed28054952fb7c12a217944420e70ffc9fbea281 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 20:59:43 -0700 Subject: [PATCH 026/214] Add tests for indexers. --- .../CSharpEditorServicesTest.csproj | 1 + ...essionBodyForIndexersRefafactoringTests.cs | 102 ++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index cfe41f9a0cbc3..ed7fba0648ab9 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -362,6 +362,7 @@ + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs new file mode 100644 index 0000000000000..61c669dfc3b09 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs @@ -0,0 +1,102 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForIndexersRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForIndexersCodeRefactoringProvider(); + + private IDictionary UseExpressionBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); + + private IDictionary UseBlockBody => + this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.NeverWithNoneEnforcement); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + int this[int i] + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + +[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int this[int i] + { + get + { + [||]return Bar(); + } + } +}", +@"class C +{ + int this[int i] => Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + Action Foo[int i] + { + get + { + return () => { [||] }; + } + } +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + int this[int i] => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int this[int i] => [||]Bar(); +}", +@"class C +{ + int this[int i] { get => Bar(); } +}", parameters: new TestParameters(options: UseExpressionBody)); + } + } +} \ No newline at end of file From 5c97815a48bfca5973d6e604b93a0bbe73dcbd79 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 21:23:53 -0700 Subject: [PATCH 027/214] Add tests for accessors. --- .../CSharpEditorServicesTest.csproj | 13 +- ...ressionBodyForAccessorsRefactoringTests.cs | 156 ++++++++++++++++ ...ionBodyForConstructorsRefactoringTests.cs} | 0 ...ForConversionOperatorsRefactoringTests.cs} | 0 ...ressionBodyForIndexersRefactoringTests.cs} | 0 ...pressionBodyForMethodsRefactoringTests.cs} | 0 ...essionBodyForOperatorsRefactoringTests.cs} | 0 ...essionBodyForPropertiesRefactoringTests.cs | 176 ++++++++++++++++++ ...sionBodyForPropertiesRefafactoringTests.cs | 102 ---------- 9 files changed, 339 insertions(+), 108 deletions(-) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs rename src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/{UseExpressionBodyForConstructorsRefafactoringTests.cs => UseExpressionBodyForConstructorsRefactoringTests.cs} (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/{UseExpressionBodyForConversionOperatorsRefafactoringTests.cs => UseExpressionBodyForConversionOperatorsRefactoringTests.cs} (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/{UseExpressionBodyForIndexersRefafactoringTests.cs => UseExpressionBodyForIndexersRefactoringTests.cs} (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/{UseExpressionBodyForMethodsRefafactoringTests.cs => UseExpressionBodyForMethodsRefactoringTests.cs} (100%) rename src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/{UseExpressionBodyForOperatorsRefafactoringTests.cs => UseExpressionBodyForOperatorsRefactoringTests.cs} (100%) create mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs delete mode 100644 src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index ed7fba0648ab9..cbd8dbefc8d4a 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -360,12 +360,13 @@ - - - - - - + + + + + + + diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs new file mode 100644 index 0000000000000..9ecce46988a58 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -0,0 +1,156 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForAccessorsRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForAccessorsCodeRefactoringProvider(); + + private IDictionary UseExpressionBodyForAccessors_BlockBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement)); + + private IDictionary UseExpressionBodyForAccessors_ExpressionBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement)); + + private IDictionary UseBlockBodyForAccessors_ExpressionBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement)); + + private IDictionary UseBlockBodyForAccessors_BlockBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement)); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody2() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { + return [||]Bar(); + } + } +}", +@"class C +{ + int Foo { get => Bar(); } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInBlockBody2() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + return [||]Bar(); + } + } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + int Foo { get => [||]Bar(); } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody2() + { + await TestMissingAsync( +@"class C +{ + int Foo { get => [||]Bar(); } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo { get => [||]Bar(); } +}", +@"class C +{ + int Foo { get { return Bar(); } } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody2() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo { get => [||]Bar(); } +}", +@"class C +{ + int Foo { get { return Bar(); } } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs similarity index 100% rename from src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefafactoringTests.cs rename to src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs new file mode 100644 index 0000000000000..b8245440f734a --- /dev/null +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs @@ -0,0 +1,176 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.CodeRefactorings; +using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; +using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; +using Microsoft.CodeAnalysis.Options; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody +{ + public class UseExpressionBodyForPropertiesRefactoringTests : AbstractCSharpCodeActionTest + { + protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) + => new UseExpressionBodyForPropertiesCodeRefactoringProvider(); + + private IDictionary UseExpressionBodyForAccessors_BlockBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement)); + + private IDictionary UseExpressionBodyForAccessors_ExpressionBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement)); + + private IDictionary UseBlockBodyForAccessors_ExpressionBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement)); + + private IDictionary UseBlockBodyForAccessors_BlockBodyForProperties => + OptionsSet( + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithNoneEnforcement), + this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement)); + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody2() + { + await TestMissingAsync( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", +@"class C +{ + int Foo => Bar(); +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody2() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { + [||]return Bar(); + } + } +}", +@"class C +{ + int Foo => Bar(); +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedInLambda() + { + await TestMissingAsync( +@"class C +{ + Action Foo + { + get + { + return () => { [||] }; + } + } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() + { + await TestMissingAsync( +@"class C +{ + int Foo => [||]Bar(); +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody2() + { + await TestMissingAsync( +@"class C +{ + int Foo => [||]Bar(); +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo => [||]Bar(); +}", +@"class C +{ + int Foo { get => Bar(); } +}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_ExpressionBodyForProperties)); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody2() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo => [||]Bar(); +}", +@"class C +{ + int Foo { get { return Bar(); } } +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs deleted file mode 100644 index 69209fe76555b..0000000000000 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefafactoringTests.cs +++ /dev/null @@ -1,102 +0,0 @@ -// 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.Tasks; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; -using Microsoft.CodeAnalysis.CSharp.UseExpressionBody; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings; -using Microsoft.CodeAnalysis.Options; -using Roslyn.Test.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody -{ - public class UseExpressionBodyForPropertiesRefactoringTests : AbstractCSharpCodeActionTest - { - protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForPropertiesCodeRefactoringProvider(); - - private IDictionary UseExpressionBody => - this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); - - private IDictionary UseBlockBody => - this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement); - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() - { - await TestMissingAsync( -@"class C -{ - int Foo - { - get - { - [||]return Bar(); - } - } -}", parameters: new TestParameters(options: UseExpressionBody)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestOfferedIfUserPrefersBlockBodiesAndInBlockBody() - { - await TestInRegularAndScript1Async( -@"class C -{ - int Foo - { - get - { - [||]return Bar(); - } - } -}", -@"class C -{ - int Foo => Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedInLambda() - { - await TestMissingAsync( -@"class C -{ - Action Foo - { - get - { - return () => { [||] }; - } - } -}", parameters: new TestParameters(options: UseBlockBody)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody() - { - await TestMissingAsync( -@"class C -{ - int Foo => [||]Bar(); -}", parameters: new TestParameters(options: UseBlockBody)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() - { - await TestInRegularAndScript1Async( -@"class C -{ - int Foo => [||]Bar(); -}", -@"class C -{ - int Foo { get => Bar(); } -}", parameters: new TestParameters(options: UseExpressionBody)); - } - } -} \ No newline at end of file From a7f876fbc7b9f2db99ae9e71181812574d9f8830 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 21:30:59 -0700 Subject: [PATCH 028/214] Remove folder. --- src/Features/CSharp/Portable/CSharpFeatures.csproj | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index 060ceb34a6e74..73df18dc0f74b 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -504,9 +504,7 @@ - - - + \ No newline at end of file From d5bdf4d99271b7c52c865678b19294993144182d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 21:40:52 -0700 Subject: [PATCH 029/214] Share code --- .../UseExpressionBodyForAccessorsHelper.cs | 65 ++++++++++--------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 0b998665f89cc..1c8a8bee3191f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.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; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; @@ -15,11 +16,28 @@ internal class UseExpressionBodyForAccessorsHelper : private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); + private readonly Func _propertyCanOfferUseExpressionBody = + UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; + + private readonly Func _propertyCanOfferUseBlockBody = + UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; + + private readonly Func _indexerCanOfferUseExpressionBody = + UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; + + private readonly Func _indexerCanOfferUseBlockBody = + UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; + + private readonly Func _baseCanOfferUseExpressionBody; + private readonly Func _baseCanOfferUseBlockBody; + public UseExpressionBodyForAccessorsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedAccessors) { + _baseCanOfferUseExpressionBody = base.CanOfferUseExpressionBody; + _baseCanOfferUseBlockBody = base.CanOfferUseBlockBody; } public override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) @@ -28,16 +46,18 @@ public override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) public override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) => declaration.ExpressionBody; - public override bool CanOfferUseExpressionBody( - OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + private bool CanOffer( + OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer, + Func propertyPredicate, + Func indexerPredicate, + Func basePredicate) { var grandParent = accessor.Parent.Parent; if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) { var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; - if (UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody( - optionSet, propertyDeclaration, forAnalyzer)) + if (propertyPredicate(optionSet, propertyDeclaration, forAnalyzer)) { return false; } @@ -45,41 +65,28 @@ public override bool CanOfferUseExpressionBody( else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) { var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; - if (UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody( - optionSet, indexerDeclaration, forAnalyzer)) + if (indexerPredicate(optionSet, indexerDeclaration, forAnalyzer)) { return false; } } - return base.CanOfferUseExpressionBody(optionSet, accessor, forAnalyzer); + return basePredicate(optionSet, accessor, forAnalyzer); + } + + public override bool CanOfferUseExpressionBody(OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + { + return CanOffer( + optionSet, accessor, forAnalyzer, + _propertyCanOfferUseExpressionBody, _indexerCanOfferUseExpressionBody, _baseCanOfferUseExpressionBody); } public override bool CanOfferUseBlockBody( OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) { - var grandParent = accessor.Parent.Parent; - - if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) - { - var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; - if (UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody( - optionSet, propertyDeclaration, forAnalyzer)) - { - return false; - } - } - else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) - { - var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; - if (UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody( - optionSet, indexerDeclaration, forAnalyzer)) - { - return false; - } - } - - return base.CanOfferUseBlockBody(optionSet, accessor, forAnalyzer); + return CanOffer( + optionSet, accessor, forAnalyzer, + _propertyCanOfferUseBlockBody, _indexerCanOfferUseBlockBody, _baseCanOfferUseBlockBody); } protected override SyntaxToken GetSemicolonToken(AccessorDeclarationSyntax declaration) From d5339fac57cce1d963c2671085a378622bde0230 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 21:44:07 -0700 Subject: [PATCH 030/214] Move to singletons. --- ...ssionBodyForAccessorsDiagnosticAnalyzer.cs | 2 +- ...onBodyForConstructorsDiagnosticAnalyzer.cs | 2 +- ...orConversionOperatorsDiagnosticAnalyzer.cs | 2 +- ...ressionBodyForMethodsDiagnosticAnalyzer.cs | 2 +- ...ssionBodyForOperatorsDiagnosticAnalyzer.cs | 2 +- ...pressionBodyForAccessorsCodeFixProvider.cs | 2 +- ...ssionBodyForConstructorsCodeFixProvider.cs | 2 +- ...dyForConversionOperatorsCodeFixProvider.cs | 2 +- ...ExpressionBodyForMethodsCodeFixProvider.cs | 2 +- ...pressionBodyForOperatorsCodeFixProvider.cs | 2 +- ...BodyForAccessorsCodeRefactoringProvider.cs | 2 +- ...yForConstructorsCodeRefactoringProvider.cs | 2 +- ...versionOperatorsCodeRefactoringProvider.cs | 2 +- ...onBodyForMethodsCodeRefactoringProvider.cs | 2 +- ...BodyForOperatorsCodeRefactoringProvider.cs | 2 +- .../UseExpressionBodyForAccessorsHelper.cs | 22 ++++++------------- .../UseExpressionBodyForConstructorsHelper.cs | 4 +++- ...ressionBodyForConversionOperatorsHelper.cs | 4 +++- .../UseExpressionBodyForMethodsHelper.cs | 4 +++- .../UseExpressionBodyForOperatorsHelper.cs | 4 +++- 20 files changed, 34 insertions(+), 34 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs index 4fbde52a1aaa8..88bf61d3621d3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs @@ -16,7 +16,7 @@ internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : public UseExpressionBodyForAccessorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration), - new UseExpressionBodyForAccessorsHelper()) + UseExpressionBodyForAccessorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs index ed714425277b6..2291a7ddb4704 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs @@ -13,7 +13,7 @@ internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : public UseExpressionBodyForConstructorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, ImmutableArray.Create(SyntaxKind.ConstructorDeclaration), - new UseExpressionBodyForConstructorsHelper()) + UseExpressionBodyForConstructorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs index ffc8ea4142820..d51a9e3c04215 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs @@ -13,7 +13,7 @@ internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, ImmutableArray.Create(SyntaxKind.ConversionOperatorDeclaration), - new UseExpressionBodyForConversionOperatorsHelper()) + UseExpressionBodyForConversionOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs index 31607db8b5b13..ba7a844c3d987 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs @@ -13,7 +13,7 @@ internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : public UseExpressionBodyForMethodsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, ImmutableArray.Create(SyntaxKind.MethodDeclaration), - new UseExpressionBodyForMethodsHelper()) + UseExpressionBodyForMethodsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs index b587575312124..80b6279472a86 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs @@ -13,7 +13,7 @@ internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : public UseExpressionBodyForOperatorsDiagnosticAnalyzer() : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, ImmutableArray.Create(SyntaxKind.OperatorDeclaration), - new UseExpressionBodyForOperatorsHelper()) + UseExpressionBodyForOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs index 7fa4433561247..5f6d329668c89 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs @@ -13,7 +13,7 @@ internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpress { public UseExpressionBodyForAccessorsCodeFixProvider() : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - new UseExpressionBodyForAccessorsHelper()) + UseExpressionBodyForAccessorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs index dd3e31908adb9..73940a41f853d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs @@ -12,7 +12,7 @@ internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpr { public UseExpressionBodyForConstructorsCodeFixProvider() : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, - new UseExpressionBodyForConstructorsHelper()) + UseExpressionBodyForConstructorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs index 2e54838852fb8..9e37730b66d3b 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs @@ -12,7 +12,7 @@ internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : Abstract { public UseExpressionBodyForConversionOperatorsCodeFixProvider() : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, - new UseExpressionBodyForConversionOperatorsHelper()) + UseExpressionBodyForConversionOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs index e1341d72b7dda..100f6b1161b8d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs @@ -12,7 +12,7 @@ internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressio { public UseExpressionBodyForMethodsCodeFixProvider() : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - new UseExpressionBodyForMethodsHelper()) + UseExpressionBodyForMethodsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs index d5d4ccd0cbbe2..41ddb16fbf04f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs @@ -12,7 +12,7 @@ internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpress { public UseExpressionBodyForOperatorsCodeFixProvider() : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - new UseExpressionBodyForOperatorsHelper()) + UseExpressionBodyForOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs index b2b180fd6cb85..d2c5e476cc133 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForAccessorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider { public UseExpressionBodyForAccessorsCodeRefactoringProvider() - : base(new UseExpressionBodyForAccessorsHelper()) + : base(UseExpressionBodyForAccessorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs index 8269679de373e..3a5dc6d5bef45 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConstructorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider { public UseExpressionBodyForConstructorsCodeRefactoringProvider() - : base(new UseExpressionBodyForConstructorsHelper()) + : base(UseExpressionBodyForConstructorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs index 378498f438b12..f6af26dd3ff1f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConversionOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider { public UseExpressionBodyForConversionOperatorsCodeRefactoringProvider() - : base(new UseExpressionBodyForConversionOperatorsHelper()) + : base(UseExpressionBodyForConversionOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs index f48454c88d545..e41a6f2dd2aa9 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForMethodsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider { public UseExpressionBodyForMethodsCodeRefactoringProvider() - : base(new UseExpressionBodyForMethodsHelper()) + : base(UseExpressionBodyForMethodsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs index 1b648dae07d08..58416bfc40bb3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider { public UseExpressionBodyForOperatorsCodeRefactoringProvider() - : base(new UseExpressionBodyForOperatorsHelper()) + : base(UseExpressionBodyForOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 1c8a8bee3191f..d9f0197ffe72a 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -1,11 +1,8 @@ // 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.Immutable; -using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody @@ -13,25 +10,20 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForAccessorsHelper : AbstractUseExpressionBodyHelper { + public static readonly UseExpressionBodyForAccessorsHelper Instance = new UseExpressionBodyForAccessorsHelper(); + private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); - private readonly Func _propertyCanOfferUseExpressionBody = - UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; - - private readonly Func _propertyCanOfferUseBlockBody = - UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; - - private readonly Func _indexerCanOfferUseExpressionBody = - UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; - - private readonly Func _indexerCanOfferUseBlockBody = - UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; + private static readonly Func _propertyCanOfferUseExpressionBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; + private static readonly Func _propertyCanOfferUseBlockBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; + private static readonly Func _indexerCanOfferUseExpressionBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; + private static readonly Func _indexerCanOfferUseBlockBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; private readonly Func _baseCanOfferUseExpressionBody; private readonly Func _baseCanOfferUseBlockBody; - public UseExpressionBodyForAccessorsHelper() + private UseExpressionBodyForAccessorsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedAccessors) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index 65dcd12f4489f..49c180ebfa28c 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -8,7 +8,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConstructorsHelper : AbstractUseExpressionBodyHelper { - public UseExpressionBodyForConstructorsHelper() + public static readonly UseExpressionBodyForConstructorsHelper Instance = new UseExpressionBodyForConstructorsHelper(); + + private UseExpressionBodyForConstructorsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedConstructors) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index 2a6b2eeaa4068..e248b5c718f53 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -8,7 +8,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConversionOperatorsHelper : AbstractUseExpressionBodyHelper { - public UseExpressionBodyForConversionOperatorsHelper() + public static readonly UseExpressionBodyForConversionOperatorsHelper Instance = new UseExpressionBodyForConversionOperatorsHelper(); + + private UseExpressionBodyForConversionOperatorsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedOperators) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index 35ebf348877b5..8925308b68f43 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -9,7 +9,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForMethodsHelper : AbstractUseExpressionBodyHelper { - public UseExpressionBodyForMethodsHelper() + public static readonly UseExpressionBodyForMethodsHelper Instance = new UseExpressionBodyForMethodsHelper(); + + private UseExpressionBodyForMethodsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedMethods) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index 2b1af26313652..a29d338c89f8a 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -8,7 +8,9 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForOperatorsHelper : AbstractUseExpressionBodyHelper { - public UseExpressionBodyForOperatorsHelper() + public static readonly UseExpressionBodyForOperatorsHelper Instance = new UseExpressionBodyForOperatorsHelper(); + + private UseExpressionBodyForOperatorsHelper() : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), CSharpCodeStyleOptions.PreferExpressionBodiedOperators) From dd5ea85ffc629b1ec07fc0e662411f091b009144 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 27 Apr 2017 21:47:24 -0700 Subject: [PATCH 031/214] Remove unused usings. --- .../UseExpressionBodyForAccessorsCodeFixProvider.cs | 1 - .../UseExpressionBodyForIndexersCodeFixProvider.cs | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs index 5f6d329668c89..473e07d6df744 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs @@ -2,7 +2,6 @@ using System.Composition; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs index 57023616eb940..3b9cec30ee235 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs @@ -1,12 +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.Composition; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { From 7f1aee3b7ecff18f710b780cb5a9485598e6eff0 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 28 Apr 2017 14:04:41 -0700 Subject: [PATCH 032/214] Add a couple of tests --- .../CSharp/Test/Emit/PDB/PDBTests.cs | 137 ++++++++++++++++++ .../Test/ExpressionCompiler/LocalsTests.cs | 22 +++ 2 files changed, 159 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs index 09de5be4eb824..a99d0900eabd4 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTests.cs @@ -2657,6 +2657,142 @@ static void Main(string[] args) " ); } + + [Fact] + public void ForEachStatement_Deconstruction() + { + var source = @" +public class C +{ + public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) }; + + public static void Main() + { + foreach (var (c, (d, e)) in F()) + { + System.Console.WriteLine(c); + } + } +} +"; + var c = CreateStandardCompilation(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll); + var v = CompileAndVerify(c); + + v.VerifyIL("C.Main", @" +{ + // Code size 72 (0x48) + .maxstack 2 + .locals init ((int, (bool, double))[] V_0, + int V_1, + int V_2, //c + bool V_3, //d + double V_4, //e + System.ValueTuple V_5) + // sequence point: { + IL_0000: nop + // sequence point: foreach + IL_0001: nop + // sequence point: F() + IL_0002: call ""(int, (bool, double))[] C.F()"" + IL_0007: stloc.0 + IL_0008: ldc.i4.0 + IL_0009: stloc.1 + // sequence point: + IL_000a: br.s IL_0041 + // sequence point: var (c, (d, e)) + IL_000c: ldloc.0 + IL_000d: ldloc.1 + IL_000e: ldelem ""System.ValueTuple"" + IL_0013: dup + IL_0014: ldfld ""(bool, double) System.ValueTuple.Item2"" + IL_0019: stloc.s V_5 + IL_001b: dup + IL_001c: ldfld ""int System.ValueTuple.Item1"" + IL_0021: stloc.2 + IL_0022: ldloc.s V_5 + IL_0024: ldfld ""bool System.ValueTuple.Item1"" + IL_0029: stloc.3 + IL_002a: ldloc.s V_5 + IL_002c: ldfld ""double System.ValueTuple.Item2"" + IL_0031: stloc.s V_4 + IL_0033: pop + // sequence point: { + IL_0034: nop + // sequence point: System.Console.WriteLine(c); + IL_0035: ldloc.2 + IL_0036: call ""void System.Console.WriteLine(int)"" + IL_003b: nop + // sequence point: } + IL_003c: nop + // sequence point: + IL_003d: ldloc.1 + IL_003e: ldc.i4.1 + IL_003f: add + IL_0040: stloc.1 + // sequence point: in + IL_0041: ldloc.1 + IL_0042: ldloc.0 + IL_0043: ldlen + IL_0044: conv.i4 + IL_0045: blt.s IL_000c + // sequence point: } + IL_0047: ret +} +", sequencePoints: "C.Main", source: source); + + v.VerifyPdb(@" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + + #endregion + + #region Switch [Fact] public void SwitchWithPattern_01() @@ -2843,6 +2979,7 @@ class Student : Person { public double GPA; } " ); } + #endregion #region DoStatement diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs index c933b603d5757..dd41414e39b75 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/LocalsTests.cs @@ -1452,6 +1452,28 @@ .locals init (C.<>c__DisplayClass1_0 V_0, //CS$<>8__locals0 }); } + [Fact(Skip = "18273"), WorkItem(18273, "https://github.com/dotnet/roslyn/issues/18273")] + public void CapturedLocalInNestedLambda() + { + var source = @" +using System; + +class C +{ + void M() { } +}"; + var compilation0 = CreateStandardCompilation(source, options: TestOptions.DebugDll); + WithRuntimeInstance(compilation0, runtime => + { + var context = CreateMethodContext(runtime, "C.M"); + + var testData = new CompilationTestData(); + context.CompileExpression("new Action(() => { int x; new Func(() => x).Invoke(); }).Invoke()", out var error, testData); + Assert.Null(error); + testData.GetMethodData("<>x.<>m0").VerifyIL(""); + }); + } + [Fact] public void NestedLambdas() { From 3de672b37753941d4623ecfb435f4bb00818a71f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 15:04:57 -0700 Subject: [PATCH 033/214] Make the SyntaxTreeIndex use a content based scheme for persisting data. --- .../Storage/PersistentStorageService.cs | 18 +- .../FindSymbols/SyntaxTree/SyntaxTreeIndex.cs | 8 +- .../SyntaxTree/SyntaxTreeIndex_Persistence.cs | 210 ++++++------------ .../IPersistentStorageService.cs | 5 + .../NoOpPersistentStorageService.cs | 5 +- .../TestPersistenceService.cs | 5 +- 6 files changed, 94 insertions(+), 157 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs b/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs index c846675c6954b..ec08c7d0dd8ca 100644 --- a/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs +++ b/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Storage /// A service that enables storing and retrieving of information associated with solutions, /// projects or documents across runtime sessions. /// - internal abstract partial class AbstractPersistentStorageService : IPersistentStorageService + internal abstract partial class AbstractPersistentStorageService : IPersistentStorageService2 { protected readonly IOptionService OptionService; private readonly SolutionSizeTracker _solutionSizeTracker; @@ -58,8 +58,11 @@ protected AbstractPersistentStorageService(IOptionService optionService, bool te protected abstract bool ShouldDeleteDatabase(Exception exception); public IPersistentStorage GetStorage(Solution solution) + => GetStorage(solution, checkBranchId: true); + + public IPersistentStorage GetStorage(Solution solution, bool checkBranchId) { - if (!ShouldUseDatabase(solution)) + if (!ShouldUseDatabase(solution, checkBranchId)) { return NoOpPersistentStorage.Instance; } @@ -136,16 +139,21 @@ private IPersistentStorage GetStorage(Solution solution, string workingFolderPat } } - private bool ShouldUseDatabase(Solution solution) + private bool ShouldUseDatabase(Solution solution, bool checkBranchId) { if (_testing) { return true; } - // we only use database for primary solution. (Ex, forked solution will not use database) - if (solution.BranchId != solution.Workspace.PrimaryBranchId || solution.FilePath == null) + if (solution.FilePath == null) + { + return false; + } + + if (checkBranchId && solution.BranchId != solution.Workspace.PrimaryBranchId) { + // we only use database for primary solution. (Ex, forked solution will not use database) return false; } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs index f9de2f0bc78ae..e490eac19b709 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs @@ -11,21 +11,21 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal sealed partial class SyntaxTreeIndex { - private readonly VersionStamp _version; - private readonly LiteralInfo _literalInfo; private readonly IdentifierInfo _identifierInfo; private readonly ContextInfo _contextInfo; private readonly DeclarationInfo _declarationInfo; private SyntaxTreeIndex( - VersionStamp version, + Checksum textChecksum, + Checksum parseOptionsChecksum, LiteralInfo literalInfo, IdentifierInfo identifierInfo, ContextInfo contextInfo, DeclarationInfo declarationInfo) { - Version = version; + TextChecksum = textChecksum; + ParseOptionsChecksum = parseOptionsChecksum; _literalInfo = literalInfo; _identifierInfo = identifierInfo; _contextInfo = contextInfo; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 4a11652d7ee15..2f88a9c2b2db4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -1,70 +1,67 @@ // 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.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Versions; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols { internal sealed partial class SyntaxTreeIndex : IObjectWritable { - private const string PersistenceName = ""; - private const string SerializationFormat = "6"; + private const string PersistenceName = ""; + private const string SerializationFormat = "7"; - /// - /// in memory cache will hold onto any info related to opened documents in primary branch or all documents in forked branch - /// - /// this is not snapshot based so multiple versions of snapshots can re-use same data as long as it is relevant. - /// - private static readonly ConditionalWeakTable> s_cache = - new ConditionalWeakTable>(); + public readonly Checksum TextChecksum; + public readonly Checksum ParseOptionsChecksum; - public readonly VersionStamp Version; - - private void WriteVersion(ObjectWriter writer, string formatVersion) + private void WriteFormatAndChecksums(ObjectWriter writer, string formatVersion) { writer.WriteString(formatVersion); - this.Version.WriteTo(writer); + TextChecksum.WriteTo(writer); + ParseOptionsChecksum.WriteTo(writer); } - private static bool TryReadVersion(ObjectReader reader, string formatVersion, out VersionStamp version) + private static bool TryReadFormatAndChecksums( + ObjectReader reader, string formatVersion, + out Checksum textChecksum, out Checksum parseOptionsChecksum) { - version = VersionStamp.Default; + textChecksum = null; + parseOptionsChecksum = null; if (reader.ReadString() != formatVersion) { return false; } - version = VersionStamp.ReadFrom(reader); + textChecksum = Checksum.ReadFrom(reader); + parseOptionsChecksum = Checksum.ReadFrom(reader); return true; } private static async Task LoadAsync( Document document, string persistenceName, string formatVersion, - Func readFrom, CancellationToken cancellationToken) + Func readFrom, CancellationToken cancellationToken) { - var persistentStorageService = document.Project.Solution.Workspace.Services.GetService(); - var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); + var solution = document.Project.Solution; + var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); + + var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); try { // attempt to load from persisted state - using (var storage = persistentStorageService.GetStorage(document.Project.Solution)) + using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { if (reader != null) { - if (TryReadVersion(reader, formatVersion, out var persistVersion) && - document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, persistVersion)) + if (DataPreambleMatches(reader, formatVersion, textChecksum, parseOptionsChecksum)) { - return readFrom(reader, syntaxVersion); + return readFrom(reader, textChecksum, parseOptionsChecksum); } } } @@ -77,20 +74,43 @@ private static async Task LoadAsync( return null; } + private static bool DataPreambleMatches( + ObjectReader reader, string formatVersion, Checksum textChecksum, Checksum parseOptionsChecksum) + { + return TryReadFormatAndChecksums(reader, formatVersion, out var persistTextChecksum, out var persistParseOptionsChecksum) && + persistTextChecksum == textChecksum && + persistParseOptionsChecksum == parseOptionsChecksum; + } + + private static async Task<(Checksum textChecksum, Checksum parseOptionsChecksum)> GetChecksumsAsync( + Document document, CancellationToken cancellationToken) + { + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); + var textChecksum = Checksum.Create(WellKnownSynchronizationKinds.SourceText, text.GetChecksum()); + + var parseOptions = document.Project.ParseOptions; + + var serializer = new Serializer(document.Project.Solution.Workspace); + var parseOptionsChecksum = ChecksumCache.GetOrCreate( + parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); + + return (textChecksum, parseOptionsChecksum); + } + private static async Task SaveAsync( Document document, string persistenceName, string formatVersion, SyntaxTreeIndex data, CancellationToken cancellationToken) { - Contract.Requires(!await document.IsForkedDocumentWithSyntaxChangesAsync(cancellationToken).ConfigureAwait(false)); - - var persistentStorageService = document.Project.Solution.Workspace.Services.GetService(); + var solution = document.Project.Solution; + var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); + var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); try { - using (var storage = persistentStorageService.GetStorage(document.Project.Solution)) + using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - data.WriteVersion(writer, formatVersion); + data.WriteFormatAndChecksums(writer, formatVersion); data.WriteTo(writer); stream.Position = 0; @@ -105,24 +125,23 @@ private static async Task SaveAsync( return false; } - private static async Task PrecalculatedAsync(Document document, string persistenceName, string formatVersion, CancellationToken cancellationToken) + private static async Task PrecalculatedAsync( + Document document, string persistenceName, string formatVersion, CancellationToken cancellationToken) { - Contract.Requires(document.IsFromPrimaryBranch()); - - var persistentStorageService = document.Project.Solution.Workspace.Services.GetService(); - var syntaxVersion = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); + var solution = document.Project.Solution; + var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); + var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); // check whether we already have info for this document try { - using (var storage = persistentStorageService.GetStorage(document.Project.Solution)) + using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { if (reader != null) { - return TryReadVersion(reader, formatVersion, out var persistVersion) && - document.CanReusePersistedSyntaxTreeVersion(syntaxVersion, persistVersion); + return DataPreambleMatches(reader, formatVersion, textChecksum, parseOptionsChecksum); } } } @@ -142,7 +161,8 @@ public void WriteTo(ObjectWriter writer) _declarationInfo.WriteTo(writer); } - private static SyntaxTreeIndex ReadFrom(ObjectReader reader, VersionStamp version) + private static SyntaxTreeIndex ReadFrom( + ObjectReader reader, Checksum textChecksum, Checksum parseOptionsChecksum) { var literalInfo = LiteralInfo.TryReadFrom(reader); var identifierInfo = IdentifierInfo.TryReadFrom(reader); @@ -155,118 +175,16 @@ private static SyntaxTreeIndex ReadFrom(ObjectReader reader, VersionStamp versio } return new SyntaxTreeIndex( - version, literalInfo.Value, identifierInfo.Value, contextInfo.Value, declarationInfo.Value); + textChecksum, parseOptionsChecksum, literalInfo.Value, identifierInfo.Value, contextInfo.Value, declarationInfo.Value); } private Task SaveAsync(Document document, CancellationToken cancellationToken) - => SaveAsync(document, s_cache, PersistenceName, SerializationFormat, cancellationToken); - - private async Task SaveAsync( - Document document, - ConditionalWeakTable> cache, - string persistenceName, - string serializationFormat, - CancellationToken cancellationToken) - { - var workspace = document.Project.Solution.Workspace; - var infoTable = GetInfoTable(document.Project.Solution.BranchId, workspace, cache); - - // if it is forked document - if (await document.IsForkedDocumentWithSyntaxChangesAsync(cancellationToken).ConfigureAwait(false)) - { - infoTable.Remove(document.Id); - infoTable.GetValue(document.Id, _ => this); - return false; - } - - // okay, cache this info if it is from opened document or persistence failed. - var persisted = await SaveAsync(document, persistenceName, serializationFormat, this, cancellationToken).ConfigureAwait(false); - if (!persisted || document.IsOpen()) - { - var primaryInfoTable = GetInfoTable(workspace.PrimaryBranchId, workspace, cache); - primaryInfoTable.Remove(document.Id); - primaryInfoTable.GetValue(document.Id, _ => this); - } - - return persisted; - } + => SaveAsync(document, PersistenceName, SerializationFormat, this, cancellationToken); private static Task LoadAsync(Document document, CancellationToken cancellationToken) - => LoadAsync(document, ReadFrom, s_cache, PersistenceName, SerializationFormat, cancellationToken); - - private static async Task LoadAsync( - Document document, - Func reader, - ConditionalWeakTable> cache, - string persistenceName, - string serializationFormat, - CancellationToken cancellationToken) - { - var infoTable = cache.GetValue( - document.Project.Solution.BranchId, - _ => new ConditionalWeakTable()); - var version = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); - // first look to see if we already have the info in the cache - if (infoTable.TryGetValue(document.Id, out var info) && info.Version == version) - { - return info; - } - - // cache is invalid. remove it - infoTable.Remove(document.Id); - - // check primary cache to see whether we have valid info there - var primaryInfoTable = cache.GetValue( - document.Project.Solution.Workspace.PrimaryBranchId, - _ => new ConditionalWeakTable()); - if (primaryInfoTable.TryGetValue(document.Id, out info) && info.Version == version) - { - return info; - } - - // check whether we can get it from persistence service - info = await LoadAsync(document, persistenceName, serializationFormat, reader, cancellationToken).ConfigureAwait(false); - if (info != null) - { - // save it in the cache. persisted info is always from primary branch. no reason to save it to the branched document cache. - primaryInfoTable.Remove(document.Id); - primaryInfoTable.GetValue(document.Id, _ => info); - return info; - } - - // well, we don't have this information. - return null; - } + => LoadAsync(document, PersistenceName, SerializationFormat, ReadFrom, cancellationToken); private static Task PrecalculatedAsync(Document document, CancellationToken cancellationToken) => PrecalculatedAsync(document, PersistenceName, SerializationFormat, cancellationToken); - - private static ConditionalWeakTable GetInfoTable( - BranchId branchId, - Workspace workspace, - ConditionalWeakTable> cache) - { - return cache.GetValue(branchId, id => - { - if (id == workspace.PrimaryBranchId) - { - workspace.DocumentClosed += (sender, e) => - { - if (!e.Document.IsFromPrimaryBranch()) - { - return; - } - - if (cache.TryGetValue(e.Document.Project.Solution.BranchId, out var infoTable)) - { - // remove closed document from primary branch from live cache. - infoTable.Remove(e.Document.Id); - } - }; - } - - return new ConditionalWeakTable(); - }); - } } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageService.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageService.cs index a5c3054978c98..dfebcf54a6d39 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/IPersistentStorageService.cs @@ -9,4 +9,9 @@ public interface IPersistentStorageService : IWorkspaceService { IPersistentStorage GetStorage(Solution solution); } + + internal interface IPersistentStorageService2 : IPersistentStorageService + { + IPersistentStorage GetStorage(Solution solution, bool checkBranchId); + } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorageService.cs b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorageService.cs index 705cd73bff87c..d183c46945090 100644 --- a/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorageService.cs +++ b/src/Workspaces/Core/Portable/Workspace/Host/PersistentStorage/NoOpPersistentStorageService.cs @@ -2,7 +2,7 @@ namespace Microsoft.CodeAnalysis.Host { - internal class NoOpPersistentStorageService : IPersistentStorageService + internal class NoOpPersistentStorageService : IPersistentStorageService2 { public static readonly IPersistentStorageService Instance = new NoOpPersistentStorageService(); @@ -12,5 +12,8 @@ private NoOpPersistentStorageService() public IPersistentStorage GetStorage(Solution solution) => NoOpPersistentStorage.Instance; + + public IPersistentStorage GetStorage(Solution solution, bool checkBranchId) + => NoOpPersistentStorage.Instance; } } \ No newline at end of file diff --git a/src/Workspaces/CoreTest/Host/WorkspaceServices/TestPersistenceService.cs b/src/Workspaces/CoreTest/Host/WorkspaceServices/TestPersistenceService.cs index f6ae8152dc923..b719c9bb38521 100644 --- a/src/Workspaces/CoreTest/Host/WorkspaceServices/TestPersistenceService.cs +++ b/src/Workspaces/CoreTest/Host/WorkspaceServices/TestPersistenceService.cs @@ -7,9 +7,12 @@ namespace Microsoft.CodeAnalysis.UnitTests.Persistence { [ExportWorkspaceService(typeof(IPersistentStorageService), "Test"), Shared] - public class TestPersistenceService : IPersistentStorageService + public class TestPersistenceService : IPersistentStorageService2 { public IPersistentStorage GetStorage(Solution solution) => NoOpPersistentStorage.Instance; + + public IPersistentStorage GetStorage(Solution solution, bool checkBranchId) + => NoOpPersistentStorage.Instance; } } \ No newline at end of file From b1d8a8c8be262f73debaa32439f78ef9687b7a94 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 15:33:48 -0700 Subject: [PATCH 034/214] Provide checksums. --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs | 5 +++-- .../FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 376ea7aa56007..7940bca7af26e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -154,10 +154,11 @@ private static async Task CreateInfoAsync(Document document, Ca } } - var version = await document.GetSyntaxVersionAsync(cancellationToken).ConfigureAwait(false); + var checksums = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); return new SyntaxTreeIndex( - version, + checksums.textChecksum, + checksums.parseOptionsChecksum, new LiteralInfo( new BloomFilter(FalsePositiveProbability, stringLiterals, longLiterals)), new IdentifierInfo( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 2f88a9c2b2db4..dcc5651327367 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -82,7 +82,7 @@ private static bool DataPreambleMatches( persistParseOptionsChecksum == parseOptionsChecksum; } - private static async Task<(Checksum textChecksum, Checksum parseOptionsChecksum)> GetChecksumsAsync( + public static async Task<(Checksum textChecksum, Checksum parseOptionsChecksum)> GetChecksumsAsync( Document document, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); From 1c43a909e92b5e563e37b421683b2f64131d309e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 15:54:09 -0700 Subject: [PATCH 035/214] Move to a content-based indexing, instead of version based indexing. --- .../AssemblySerializationInfoService.cs | 6 +- .../FindSymbols/SymbolTree/SymbolTreeInfo.cs | 28 +++++----- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 26 ++++++--- .../SymbolTreeInfo_Serialization.cs | 55 ++++++++++--------- .../SymbolTree/SymbolTreeInfo_Source.cs | 7 ++- .../AssemblySerializationInfoService.cs | 4 +- .../IAssemblySerializationInfoService.cs | 2 +- .../Core/Portable/Utilities/SpellChecker.cs | 18 +++--- .../CoreTest/FindAllDeclarationsTests.cs | 4 +- 9 files changed, 81 insertions(+), 69 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs b/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs index bfcc341c8a38c..77e84e7df16b9 100644 --- a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs @@ -27,12 +27,10 @@ public bool Serializable(Solution solution, string assemblyFilePath) return true; } - public bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version) + public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) { prefix = PathUtilities.GetRelativePath(solution.FilePath, assemblyFilePath); - version = VersionStamp.Create(File.GetLastWriteTimeUtc(assemblyFilePath)); - return true; } } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 0e8daa8c9ad1b..09ff46e11e176 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { - private readonly VersionStamp _version; + private readonly Checksum _checksum; /// /// To prevent lots of allocations, we concatenate all the names in all our @@ -80,35 +80,35 @@ internal partial class SymbolTreeInfo }; private SymbolTreeInfo( - VersionStamp version, + Checksum checksum, string concatenatedNames, Node[] sortedNodes, Task spellCheckerTask, OrderPreservingMultiDictionary inheritanceMap) - : this(version, concatenatedNames, sortedNodes, spellCheckerTask) + : this(checksum, concatenatedNames, sortedNodes, spellCheckerTask) { var indexBasedInheritanceMap = CreateIndexBasedInheritanceMap(inheritanceMap); _inheritanceMap = indexBasedInheritanceMap; } private SymbolTreeInfo( - VersionStamp version, + Checksum checksum, string concatenatedNames, Node[] sortedNodes, Task spellCheckerTask, OrderPreservingMultiDictionary inheritanceMap) - : this(version, concatenatedNames, sortedNodes, spellCheckerTask) + : this(checksum, concatenatedNames, sortedNodes, spellCheckerTask) { _inheritanceMap = inheritanceMap; } private SymbolTreeInfo( - VersionStamp version, + Checksum checksum, string concatenatedNames, Node[] sortedNodes, Task spellCheckerTask) { - _version = version; + _checksum = checksum; _concatenatedNames = concatenatedNames; _nodes = ImmutableArray.Create(sortedNodes); _spellCheckerTask = spellCheckerTask; @@ -315,15 +315,15 @@ private int BinarySearch(string name) _ => new SemaphoreSlim(1); private static Task GetSpellCheckerTask( - Solution solution, VersionStamp version, string filePath, + Solution solution, Checksum checksum, string filePath, string concatenatedNames, Node[] sortedNodes) { // Create a new task to attempt to load or create the spell checker for this // SymbolTreeInfo. This way the SymbolTreeInfo will be ready immediately // for non-fuzzy searches, and soon afterwards it will be able to perform // fuzzy searches as well. - return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, filePath, - v => new SpellChecker(v, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan))))); + return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, checksum, filePath, + () => new SpellChecker(checksum, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan))))); } private static void SortNodes( @@ -472,7 +472,7 @@ private string GetName(Node node) internal void AssertEquivalentTo(SymbolTreeInfo other) { - Debug.Assert(_version.Equals(other._version)); + Debug.Assert(_checksum.Equals(other._checksum)); Debug.Assert(_concatenatedNames == other._concatenatedNames); Debug.Assert(_nodes.Length == other._nodes.Length); @@ -499,16 +499,16 @@ internal void AssertEquivalentTo(SymbolTreeInfo other) } private static SymbolTreeInfo CreateSymbolTreeInfo( - Solution solution, VersionStamp version, + Solution solution, Checksum checksum, string filePath, ImmutableArray unsortedNodes, OrderPreservingMultiDictionary inheritanceMap) { SortNodes(unsortedNodes, out var concatenatedNames, out var sortedNodes); var createSpellCheckerTask = GetSpellCheckerTask( - solution, version, filePath, concatenatedNames, sortedNodes); + solution, checksum, filePath, concatenatedNames, sortedNodes); return new SymbolTreeInfo( - version, concatenatedNames, sortedNodes, createSpellCheckerTask, inheritanceMap); + checksum, concatenatedNames, sortedNodes, createSpellCheckerTask, inheritanceMap); } private OrderPreservingMultiDictionary CreateIndexBasedInheritanceMap( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 3a96404e0f923..ebf2da0bacc26 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; @@ -131,24 +132,33 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( CancellationToken cancellationToken) { var filePath = reference.FilePath; + var checksum = IOUtilities.PerformIO(() => + { + using (var stream = File.OpenRead(filePath)) + { + return Checksum.Create(stream); + } + }, null); + return LoadOrCreateAsync( solution, + checksum, filePath, loadOnly, - create: version => CreateMetadataSymbolTreeInfo(solution, version, reference, cancellationToken), + create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken), keySuffix: "", - getVersion: info => info._version, + getPersistedChecksum: info => info._checksum, readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } private static SymbolTreeInfo CreateMetadataSymbolTreeInfo( - Solution solution, VersionStamp version, + Solution solution, Checksum checksum, PortableExecutableReference reference, CancellationToken cancellationToken) { - var creator = new MetadataInfoCreator(solution, version, reference, cancellationToken); + var creator = new MetadataInfoCreator(solution, checksum, reference, cancellationToken); return creator.Create(); } @@ -158,7 +168,7 @@ private struct MetadataInfoCreator : IDisposable private static ObjectPool> s_stringListPool = new ObjectPool>(() => new List()); private readonly Solution _solution; - private readonly VersionStamp _version; + private readonly Checksum _checksum; private readonly PortableExecutableReference _reference; private readonly CancellationToken _cancellationToken; @@ -173,10 +183,10 @@ private struct MetadataInfoCreator : IDisposable private readonly List _allTypeDefinitions; public MetadataInfoCreator( - Solution solution, VersionStamp version, PortableExecutableReference reference, CancellationToken cancellationToken) + Solution solution, Checksum checksum, PortableExecutableReference reference, CancellationToken cancellationToken) { _solution = solution; - _version = version; + _checksum = checksum; _reference = reference; _cancellationToken = cancellationToken; _metadataReader = null; @@ -239,7 +249,7 @@ internal SymbolTreeInfo Create() var unsortedNodes = GenerateUnsortedNodes(); return SymbolTreeInfo.CreateSymbolTreeInfo( - _solution, _version, _reference.FilePath, unsortedNodes, _inheritanceMap); + _solution, _checksum, _reference.FilePath, unsortedNodes, _inheritanceMap); } public void Dispose() diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 7480fc8478569..b7da954dd209c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -9,6 +10,7 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Serialization; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; @@ -17,7 +19,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { - private const string PrefixMetadataSymbolTreeInfo = "_"; + private const string PrefixMetadataSymbolTreeInfo = ""; private const string SerializationFormat = "15"; /// @@ -27,18 +29,20 @@ internal partial class SymbolTreeInfo private static Task LoadOrCreateSourceSymbolTreeInfoAsync( Solution solution, IAssemblySymbol assembly, + Checksum checksum, string filePath, bool loadOnly, CancellationToken cancellationToken) { return LoadOrCreateAsync( solution, + checksum, filePath, loadOnly, - create: version => CreateSourceSymbolTreeInfo(solution, version, assembly, filePath, cancellationToken), + create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken), keySuffix: "", - getVersion: info => info._version, - readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)), + getPersistedChecksum: info => info._checksum, + readObject: reader => ReadSymbolTreeInfo(reader, (c, names, nodes) => GetSpellCheckerTask(solution, c, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } @@ -49,16 +53,18 @@ private static Task LoadOrCreateSourceSymbolTreeInfoAsync( /// private static Task LoadOrCreateSpellCheckerAsync( Solution solution, + Checksum checksum, string filePath, - Func create) + Func create) { return LoadOrCreateAsync( solution, + checksum, filePath, loadOnly: false, create: create, - keySuffix: "SpellChecker", - getVersion: s => s.Version, + keySuffix: "_SpellChecker", + getPersistedChecksum: s => s.Checksum, readObject: SpellChecker.ReadFrom, writeObject: (w, i) => i.WriteTo(w), cancellationToken: CancellationToken.None); @@ -70,27 +76,28 @@ private static Task LoadOrCreateSpellCheckerAsync( /// private static async Task LoadOrCreateAsync( Solution solution, + Checksum checksum, string filePath, bool loadOnly, - Func create, + Func create, string keySuffix, - Func getVersion, + Func getPersistedChecksum, Func readObject, Action writeObject, CancellationToken cancellationToken) where T : class { // See if we can even use serialization. If not, we'll just have to make the value // from scratch. - if (ShouldCreateFromScratch(solution, filePath, out var prefix, out var version, cancellationToken)) + if (checksum == null || ShouldCreateFromScratch(solution, filePath, out var prefix, cancellationToken)) { - return loadOnly ? null : create(VersionStamp.Default); + return loadOnly ? null : create(); } // Ok, we can use persistence. First try to load from the persistence service. - var persistentStorageService = solution.Workspace.Services.GetService(); + var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); T result; - using (var storage = persistentStorageService.GetStorage(solution)) + using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) { // Get the unique key to identify our data. var key = PrefixMetadataSymbolTreeInfo + prefix + keySuffix; @@ -103,7 +110,7 @@ private static async Task LoadOrCreateAsync( // If we're able to, and the version of the persisted data matches // our version, then we can reuse this instance. result = readObject(reader); - if (result != null && VersionStamp.CanReusePersistedVersion(version, getVersion(result))) + if (result != null && checksum == getPersistedChecksum(result)) { return result; } @@ -121,7 +128,7 @@ private static async Task LoadOrCreateAsync( } // Now, try to create a new instance and write it to the persistence service. - result = create(version); + result = create(); if (result != null) { using (var stream = SerializableBytes.CreateWritableStream()) @@ -142,11 +149,9 @@ private static bool ShouldCreateFromScratch( Solution solution, string filePath, out string prefix, - out VersionStamp version, CancellationToken cancellationToken) { prefix = null; - version = default(VersionStamp); var service = solution.Workspace.Services.GetService(); if (service == null) @@ -160,7 +165,7 @@ private static bool ShouldCreateFromScratch( return true; } - if (!service.TryGetSerializationPrefixAndVersion(solution, filePath, out prefix, out version)) + if (!service.TryGetSerializationPrefix(solution, filePath, out prefix)) { return true; } @@ -171,7 +176,7 @@ private static bool ShouldCreateFromScratch( public void WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); - _version.WriteTo(writer); + _checksum.WriteTo(writer); writer.WriteString(_concatenatedNames); @@ -199,20 +204,20 @@ public void WriteTo(ObjectWriter writer) internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(ObjectReader reader) { return ReadSymbolTreeInfo(reader, - (version, names, nodes) => Task.FromResult( - new SpellChecker(version, nodes.Select(n => new StringSlice(names, n.NameSpan))))); + (checksum, names, nodes) => Task.FromResult( + new SpellChecker(checksum, nodes.Select(n => new StringSlice(names, n.NameSpan))))); } private static SymbolTreeInfo ReadSymbolTreeInfo( ObjectReader reader, - Func> createSpellCheckerTask) + Func> createSpellCheckerTask) { try { var formatVersion = reader.ReadString(); if (string.Equals(formatVersion, SerializationFormat, StringComparison.Ordinal)) { - var version = VersionStamp.ReadFrom(reader); + var checksum = Checksum.ReadFrom(reader); var concatenatedNames = reader.ReadString(); @@ -241,8 +246,8 @@ private static SymbolTreeInfo ReadSymbolTreeInfo( } } - var spellCheckerTask = createSpellCheckerTask(version, concatenatedNames, nodes); - return new SymbolTreeInfo(version, concatenatedNames, nodes, spellCheckerTask, inheritanceMap); + var spellCheckerTask = createSpellCheckerTask(checksum, concatenatedNames, nodes); + return new SymbolTreeInfo(checksum, concatenatedNames, nodes, spellCheckerTask, inheritanceMap); } } catch diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 0139233a6bba8..ef856d9962a5c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -28,14 +28,15 @@ public static async Task GetInfoForSourceAssemblyAsync( Project project, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var checksum = await project.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); return await LoadOrCreateSourceSymbolTreeInfoAsync( - project.Solution, compilation.Assembly, project.FilePath, + project.Solution, compilation.Assembly, checksum, project.FilePath, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); } internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( - Solution solution, VersionStamp version, IAssemblySymbol assembly, + Solution solution, Checksum checksum, IAssemblySymbol assembly, string filePath, CancellationToken cancellationToken) { if (assembly == null) @@ -49,7 +50,7 @@ internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate); return CreateSymbolTreeInfo( - solution, version, filePath, unsortedNodes.ToImmutableAndFree(), + solution, checksum, filePath, unsortedNodes.ToImmutableAndFree(), inheritanceMap: new OrderPreservingMultiDictionary()); } diff --git a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs index 5ef2c77778d29..1122ce7629f62 100644 --- a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs +++ b/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs @@ -14,11 +14,9 @@ public bool Serializable(Solution solution, string assemblyFilePath) return false; } - public bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version) + public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) { prefix = string.Empty; - version = VersionStamp.Default; - return false; } } diff --git a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs index 26ae70e84d2b1..396d5f8c94086 100644 --- a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs +++ b/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs @@ -7,6 +7,6 @@ namespace Microsoft.CodeAnalysis.Serialization internal interface IAssemblySerializationInfoService : IWorkspaceService { bool Serializable(Solution solution, string assemblyFilePath); - bool TryGetSerializationPrefixAndVersion(Solution solution, string assemblyFilePath, out string prefix, out VersionStamp version); + bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix); } } diff --git a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs index f4438e12f0eb3..77666df302cef 100644 --- a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs +++ b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs @@ -12,19 +12,19 @@ namespace Roslyn.Utilities { internal class SpellChecker { - private const string SerializationFormat = "2"; + private const string SerializationFormat = "3"; - public VersionStamp Version { get; } + public Checksum Checksum { get; } private readonly BKTree _bkTree; - public SpellChecker(VersionStamp version, BKTree bKTree) + public SpellChecker(Checksum checksum, BKTree bKTree) { - Version = version; + Checksum = checksum; _bkTree = bKTree; } - public SpellChecker(VersionStamp version, IEnumerable corpus) - : this(version, BKTree.Create(corpus)) + public SpellChecker(Checksum checksum, IEnumerable corpus) + : this(checksum, BKTree.Create(corpus)) { } @@ -45,7 +45,7 @@ public IList FindSimilarWords(string value, bool substringsAreSimilar) internal void WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); - Version.WriteTo(writer); + Checksum.WriteTo(writer); _bkTree.WriteTo(writer); } @@ -56,11 +56,11 @@ internal static SpellChecker ReadFrom(ObjectReader reader) var formatVersion = reader.ReadString(); if (string.Equals(formatVersion, SerializationFormat, StringComparison.Ordinal)) { - var version = VersionStamp.ReadFrom(reader); + var checksum = Checksum.ReadFrom(reader); var bkTree = BKTree.ReadFrom(reader); if (bkTree != null) { - return new SpellChecker(version, bkTree); + return new SpellChecker(checksum, bkTree); } } } diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs index db3b052ac75df..cf1ca2cb38a4b 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.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.Immutable; using System.IO; using System.Linq; using System.Threading; @@ -541,9 +542,8 @@ public async Task TestSymbolTreeInfoSerialization() ////var assembly = compilation.Assembly; // create symbol tree info from assembly - var version = VersionStamp.Create(); var info = SymbolTreeInfo.CreateSourceSymbolTreeInfo( - solution, version, assembly, "", cancellationToken: CancellationToken.None); + solution, Checksum.Null, assembly, "", cancellationToken: CancellationToken.None); using (var writerStream = new MemoryStream()) { From feb292917f67aa43ddc6e859072cb6de03844842 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 15:55:59 -0700 Subject: [PATCH 036/214] Change persistence version. --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index b7da954dd209c..106fa915e9cb3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -1,8 +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; -using System.Collections.Immutable; -using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -10,7 +8,6 @@ using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Serialization; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; @@ -20,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class SymbolTreeInfo { private const string PrefixMetadataSymbolTreeInfo = ""; - private const string SerializationFormat = "15"; + private const string SerializationFormat = "16"; /// /// Loads the SymbolTreeInfo for a given assembly symbol (metadata or project). If the From 5972e84775ae29f96a37a6793176306ac674c802 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 15:56:45 -0700 Subject: [PATCH 037/214] use unique suffixes. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs | 2 +- .../FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index ebf2da0bacc26..e50d112fcb67c 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -146,7 +146,7 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( filePath, loadOnly, create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken), - keySuffix: "", + keySuffix: "_Metadata", getPersistedChecksum: info => info._checksum, readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 106fa915e9cb3..ef9622102956a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -37,7 +37,7 @@ private static Task LoadOrCreateSourceSymbolTreeInfoAsync( filePath, loadOnly, create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken), - keySuffix: "", + keySuffix: "_Source", getPersistedChecksum: info => info._checksum, readObject: reader => ReadSymbolTreeInfo(reader, (c, names, nodes) => GetSpellCheckerTask(solution, c, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), From f9d55112addb3517ea0fcee97c82fc96019107f2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 28 Apr 2017 16:51:19 -0700 Subject: [PATCH 038/214] Export otion serializers for tests. --- .../Core/Portable/Collections/ArrayBuilderExtensions.cs | 2 +- src/EditorFeatures/TestUtilities/TestExportProvider.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs b/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs index c11d1e8dd9dbe..c71a5e9f62d53 100644 --- a/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs +++ b/src/Compilers/Core/Portable/Collections/ArrayBuilderExtensions.cs @@ -161,4 +161,4 @@ public static void AddIfNotNull(this ArrayBuilder builder, T value) } } } -} +} \ No newline at end of file diff --git a/src/EditorFeatures/TestUtilities/TestExportProvider.cs b/src/EditorFeatures/TestUtilities/TestExportProvider.cs index 5451bfbcc8d90..47ded14219e30 100644 --- a/src/EditorFeatures/TestUtilities/TestExportProvider.cs +++ b/src/EditorFeatures/TestUtilities/TestExportProvider.cs @@ -72,6 +72,8 @@ private static Type[] GetNeutralAndCSharpAndVisualBasicTypes() typeof(CodeAnalysis.VisualBasic.CodeGeneration.VisualBasicSyntaxGenerator), typeof(CSharp.LanguageServices.CSharpContentTypeLanguageService), typeof(VisualBasic.LanguageServices.VisualBasicContentTypeLanguageService), + typeof(CodeAnalysis.CSharp.Execution.CSharpOptionsSerializationService), + typeof(CodeAnalysis.VisualBasic.Execution.VisualBasicOptionsSerializationService), typeof(TestExportProvider) }; From 9545d10b73f15595a45b6cfd964bedab446748f2 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 29 Apr 2017 07:18:56 -0500 Subject: [PATCH 039/214] Avoid inefficient use of ContainsKey --- .../DiagnosticAnalyzer/AnalyzerManager.cs | 4 ++-- .../ImmutableSetWithInsertionOrder`1.cs | 7 ++++--- .../VisualBasicCommandLineParser.vb | 10 ++-------- .../CodeFixes/CodeFixService.cs | 19 ++++++++++++++++++- ...ousTaggerProvider.TagSource_ProduceTags.cs | 4 ++-- 5 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs index 6366c8544313d..2395d27632906 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.cs @@ -215,9 +215,9 @@ internal bool IsDiagnosticAnalyzerSuppressed( var isSuppressed = !diag.IsEnabledByDefault; // If the user said something about it, that overrides the author. - if (diagnosticOptions.ContainsKey(diag.Id)) + if (diagnosticOptions.TryGetValue(diag.Id, out var severity)) { - isSuppressed = diagnosticOptions[diag.Id] == ReportDiagnostic.Suppress; + isSuppressed = severity == ReportDiagnostic.Suppress; } if (!isSuppressed) diff --git a/src/Compilers/Core/Portable/InternalUtilities/ImmutableSetWithInsertionOrder`1.cs b/src/Compilers/Core/Portable/InternalUtilities/ImmutableSetWithInsertionOrder`1.cs index c224186a7db96..6b124ec981f7c 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/ImmutableSetWithInsertionOrder`1.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/ImmutableSetWithInsertionOrder`1.cs @@ -44,13 +44,14 @@ public ImmutableSetWithInsertionOrder Add(T value) public ImmutableSetWithInsertionOrder Remove(T value) { - // no reason to cause allocations if value is missing - if (!_map.ContainsKey(value)) + var modifiedMap = _map.Remove(value); + if (modifiedMap == _map) { + // no reason to cause allocations if value is missing return this; } - return this.Count == 1 ? Empty : new ImmutableSetWithInsertionOrder(_map.Remove(value), _nextElementValue); + return this.Count == 1 ? Empty : new ImmutableSetWithInsertionOrder(modifiedMap, _nextElementValue); } public IEnumerable InInsertionOrder diff --git a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb index b9f98a085d044..de04a2d14d671 100644 --- a/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb +++ b/src/Compilers/VisualBasic/Portable/CommandLine/VisualBasicCommandLineParser.vb @@ -1916,20 +1916,14 @@ lVbRuntimePlus: End If ' Expression evaluated successfully --> add to 'defines' - If defines.ContainsKey(symbolName) Then - defines = defines.Remove(symbolName) - End If - defines = defines.Add(symbolName, value) + defines = defines.SetItem(symbolName, value) ElseIf tokens.Current.Kind = SyntaxKind.CommaToken OrElse tokens.Current.Kind = SyntaxKind.ColonToken OrElse tokens.Current.Kind = SyntaxKind.EndOfFileToken Then ' We have no value being assigned, so we'll just assign it to true - If defines.ContainsKey(symbolName) Then - defines = defines.Remove(symbolName) - End If - defines = defines.Add(symbolName, InternalSyntax.CConst.Create(True)) + defines = defines.SetItem(symbolName, InternalSyntax.CConst.Create(True)) ElseIf tokens.Current.Kind = SyntaxKind.BadToken Then GetErrorStringForRemainderOfConditionalCompilation(tokens, parsedTokensAsString) diff --git a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs index c97332511ba64..6c70c68e4bf9c 100644 --- a/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs +++ b/src/EditorFeatures/Core/Implementation/CodeFixes/CodeFixService.cs @@ -150,7 +150,24 @@ await AppendFixesAsync( { // sort the result to the order defined by the fixers var priorityMap = _fixerPriorityMap[document.Project.Language].Value; - result.Sort((d1, d2) => priorityMap.ContainsKey((CodeFixProvider)d1.Provider) ? (priorityMap.ContainsKey((CodeFixProvider)d2.Provider) ? priorityMap[(CodeFixProvider)d1.Provider] - priorityMap[(CodeFixProvider)d2.Provider] : -1) : 1); + result.Sort((d1, d2) => + { + if (priorityMap.TryGetValue((CodeFixProvider)d1.Provider, out int priority1)) + { + if (priorityMap.TryGetValue((CodeFixProvider)d2.Provider, out int priority2)) + { + return priority1 - priority2; + } + else + { + return -1; + } + } + else + { + return 1; + } + }); } // TODO (https://github.com/dotnet/roslyn/issues/4932): Don't restrict CodeFixes in Interactive diff --git a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs index 6aea6a5cfd0cb..2015de19d6089 100644 --- a/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs +++ b/src/EditorFeatures/Core/Tagging/AbstractAsynchronousTaggerProvider.TagSource_ProduceTags.cs @@ -604,9 +604,9 @@ private void ProcessNewTagTrees( { var snapshot = spansToTag.First(s => s.SnapshotSpan.Snapshot.TextBuffer == latestBuffer).SnapshotSpan.Snapshot; - if (oldTagTrees.ContainsKey(latestBuffer)) + if (oldTagTrees.TryGetValue(latestBuffer, out var previousSpans)) { - var difference = ComputeDifference(snapshot, newTagTrees[latestBuffer], oldTagTrees[latestBuffer]); + var difference = ComputeDifference(snapshot, newTagTrees[latestBuffer], previousSpans); bufferToChanges[latestBuffer] = difference; } else From d16848a1c1d5aa5d9202b0e9308b5c747cafca4a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 29 Apr 2017 08:17:30 -0500 Subject: [PATCH 040/214] Add Deconstruct extension method for KeyValuePair --- .../Core/Portable/InternalUtilities/KeyValuePair.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Compilers/Core/Portable/InternalUtilities/KeyValuePair.cs b/src/Compilers/Core/Portable/InternalUtilities/KeyValuePair.cs index f9aa2d2b3e603..1d382ef87136b 100644 --- a/src/Compilers/Core/Portable/InternalUtilities/KeyValuePair.cs +++ b/src/Compilers/Core/Portable/InternalUtilities/KeyValuePair.cs @@ -10,5 +10,11 @@ public static KeyValuePair Create(K key, V value) { return new KeyValuePair(key, value); } + + public static void Deconstruct(this KeyValuePair keyValuePair, out TKey key, out TValue value) + { + key = keyValuePair.Key; + value = keyValuePair.Value; + } } } From ff2919d73fca5b653b17ec1984c1dc63443f87e9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 29 Apr 2017 07:53:02 -0500 Subject: [PATCH 041/214] Avoid allocations due to the use of Keys instead of GetEnumerator() --- .../Core/Portable/DiagnosticAnalyzer/AnalysisState.cs | 2 +- .../DiagnosticAnalyzer/AnalyzerFileReference.cs | 2 +- ...synchronousTaggerProvider.TagSource_ProduceTags.cs | 10 +++++----- .../Diagnostics/AbstractHostDiagnosticUpdateSource.cs | 2 +- ...sticIncrementalAnalyzer.StateManager.HostStates.cs | 3 ++- .../Suppression/VisualStudioSuppressionFixService.cs | 2 +- .../CodeFixes/FixAllOccurrences/FixAllLogger.cs | 4 ++-- .../Core/Portable/Diagnostics/Extensions.cs | 11 ++++------- .../Core/Portable/Workspace/Solution/ProjectState.cs | 2 +- 9 files changed, 18 insertions(+), 20 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs index daae9320e2c1c..da56ea41451e6 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalysisState.cs @@ -402,7 +402,7 @@ private async Task EnsureAnalyzerActionCountsInitializedAsync(AnalyzerDriver dri if (_lazyAnalyzerActionCountsMap == null) { var builder = ImmutableDictionary.CreateBuilder(); - foreach (var analyzer in _analyzerStateMap.Keys) + foreach (var (analyzer, _) in _analyzerStateMap) { var actionCounts = await driver.GetAnalyzerActionCountsAsync(analyzer, _compilationOptions, cancellationToken).ConfigureAwait(false); builder.Add(analyzer, actionCounts); diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs index 3efea68c576fd..6e46854bd53ad 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerFileReference.cs @@ -405,7 +405,7 @@ internal void AddExtensions(ImmutableDictionary(); using (Logger.LogBlock(FunctionId.Tagger_TagSource_ProcessNewTags, cancellationToken)) { - foreach (var latestBuffer in newTagTrees.Keys) + foreach (var (latestBuffer, latestSpans) in newTagTrees) { var snapshot = spansToTag.First(s => s.SnapshotSpan.Snapshot.TextBuffer == latestBuffer).SnapshotSpan.Snapshot; if (oldTagTrees.TryGetValue(latestBuffer, out var previousSpans)) { - var difference = ComputeDifference(snapshot, newTagTrees[latestBuffer], previousSpans); + var difference = ComputeDifference(snapshot, latestSpans, previousSpans); bufferToChanges[latestBuffer] = difference; } else { // It's a new buffer, so report all spans are changed - bufferToChanges[latestBuffer] = new DiffResult(added: newTagTrees[latestBuffer].GetSpans(snapshot).Select(t => t.Span), removed: null); + bufferToChanges[latestBuffer] = new DiffResult(added: latestSpans.GetSpans(snapshot).Select(t => t.Span), removed: null); } } - foreach (var oldBuffer in oldTagTrees.Keys) + foreach (var (oldBuffer, previousSpans) in oldTagTrees) { if (!newTagTrees.ContainsKey(oldBuffer)) { // This buffer disappeared, so let's notify that the old tags are gone - bufferToChanges[oldBuffer] = new DiffResult(added: null, removed: oldTagTrees[oldBuffer].GetSpans(oldBuffer.CurrentSnapshot).Select(t => t.Span)); + bufferToChanges[oldBuffer] = new DiffResult(added: null, removed: previousSpans.GetSpans(oldBuffer.CurrentSnapshot).Select(t => t.Span)); } } } diff --git a/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs b/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs index ec520c252c0fa..b1e97f4c5628c 100644 --- a/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs +++ b/src/Features/Core/Portable/Diagnostics/AbstractHostDiagnosticUpdateSource.cs @@ -101,7 +101,7 @@ public void ClearAnalyzerDiagnostics(ImmutableArray analyzer public void ClearAnalyzerDiagnostics(ProjectId projectId) { - foreach (var analyzer in _analyzerHostDiagnosticsMap.Keys) + foreach (var (analyzer, _) in _analyzerHostDiagnosticsMap) { ClearAnalyzerDiagnostics(analyzer, projectId); } diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index fa0ccc66fcbc6..340e6f489efb5 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Diagnostics.EngineV2 { @@ -95,7 +96,7 @@ public IEnumerable GetAnalyzers() yield return _compilerAnalyzer; } - foreach (var analyzer in _map.Keys) + foreach (var (analyzer, _) in _map) { yield return analyzer; } diff --git a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs index e7874fe6e52af..27d826135cee9 100644 --- a/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs +++ b/src/VisualStudio/Core/Def/Implementation/TableDataSource/Suppression/VisualStudioSuppressionFixService.cs @@ -276,7 +276,7 @@ private bool ApplySuppressionFix(IEnumerable diagnosticsToFix, F // We have different suppression fixers for every language. // So we need to group diagnostics by the containing project language and apply fixes separately. - languages = new HashSet(projectDiagnosticsToFixMap.Keys.Select(p => p.Language).Concat(documentDiagnosticsToFixMap.Select(kvp => kvp.Key.Project.Language))); + languages = new HashSet(projectDiagnosticsToFixMap.Select(p => p.Key.Language).Concat(documentDiagnosticsToFixMap.Select(kvp => kvp.Key.Project.Language))); foreach (var language in languages) { diff --git a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllLogger.cs b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllLogger.cs index bc6f24eb445bc..c1af48119044c 100644 --- a/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllLogger.cs +++ b/src/Workspaces/Core/Portable/CodeFixes/FixAllOccurrences/FixAllLogger.cs @@ -112,7 +112,7 @@ public static void LogDiagnosticsStats(ImmutableDictionary { - m[s_documentsWithDiagnosticsToFix] = documentsAndDiagnosticsToFixMap.Keys.Count(); + m[s_documentsWithDiagnosticsToFix] = documentsAndDiagnosticsToFixMap.Count; m[s_totalDiagnosticsToFix] = documentsAndDiagnosticsToFixMap.Values.Sum(v => v.Length); })); } @@ -121,7 +121,7 @@ public static void LogDiagnosticsStats(ImmutableDictionary { - m[s_projectsWithDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Keys.Count(); + m[s_projectsWithDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Count; m[s_totalDiagnosticsToFix] = projectsAndDiagnosticsToFixMap.Values.Sum(v => v.Length); })); } diff --git a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs index ab5d278cde284..f070ce9035bbe 100644 --- a/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs +++ b/src/Workspaces/Core/Portable/Diagnostics/Extensions.cs @@ -174,7 +174,6 @@ public static ImmutableDictionary(); ImmutableArray diagnostics; - ImmutableDictionary> diagnosticsByAnalyzerMap; foreach (var analyzer in analyzers) { @@ -182,20 +181,18 @@ public static ImmutableDictionary Date: Sat, 29 Apr 2017 08:14:49 -0500 Subject: [PATCH 042/214] Avoid allocations due to the use of Values instead of GetEnumerator() --- src/Compilers/CSharp/Portable/Binder/Imports.cs | 6 +++--- .../VisualBasic/Portable/Binding/Binder_Statements.vb | 3 ++- ...agnosticIncrementalAnalyzer.StateManager.HostStates.cs | 2 +- .../SolutionCrawler/AggregateIncrementalAnalyzer.cs | 7 ++++--- .../Core/Portable/Workspace/Solution/ProjectState.cs | 8 ++++---- .../Core/Portable/Workspace/Solution/SolutionState.cs | 2 +- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Imports.cs b/src/Compilers/CSharp/Portable/Binder/Imports.cs index 0da46a899103f..b0f32f2954ba7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Imports.cs +++ b/src/Compilers/CSharp/Portable/Binder/Imports.cs @@ -564,13 +564,13 @@ private void Validate() // Check constraints within named aliases. // Force resolution of named aliases. - foreach (var alias in UsingAliases.Values) + foreach (var (_, alias) in UsingAliases) { alias.Alias.GetAliasTarget(basesBeingResolved: null); semanticDiagnostics.AddRange(alias.Alias.AliasTargetDiagnostics); } - foreach (var alias in UsingAliases.Values) + foreach (var (_, alias) in UsingAliases) { alias.Alias.CheckConstraints(semanticDiagnostics); } @@ -823,7 +823,7 @@ internal void AddLookupSymbolsInfo(LookupSymbolsInfo result, LookupOptions optio internal void AddLookupSymbolsInfoInAliases(LookupSymbolsInfo result, LookupOptions options, Binder originalBinder) { - foreach (var usingAlias in this.UsingAliases.Values) + foreach (var (_, usingAlias) in this.UsingAliases) { AddAliasSymbolToResult(result, usingAlias.Alias, options, originalBinder); } diff --git a/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb b/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb index cd71413124013..5d232b3fd5ac6 100644 --- a/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb +++ b/src/Compilers/VisualBasic/Portable/Binding/Binder_Statements.vb @@ -617,7 +617,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Next If staticLocals IsNot Nothing Then - For Each array In staticLocals.Values + For Each nameToArray In staticLocals + Dim array = nameToArray.Value If array.Count > 1 Then Dim lexicallyFirst As LocalSymbol = array(0) diff --git a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs index 340e6f489efb5..936df0f01ac57 100644 --- a/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs +++ b/src/Features/Core/Portable/Diagnostics/EngineV2/DiagnosticIncrementalAnalyzer.StateManager.HostStates.cs @@ -112,7 +112,7 @@ public IEnumerable GetStateSets() } // TODO: for now, this is static, but in future, we might consider making this a dynamic so that we process cheaper analyzer first. - foreach (var set in _map.Values) + foreach (var (_, set) in _map) { yield return set; } diff --git a/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs b/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs index a73d7d438f8ac..56969eea12210 100644 --- a/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs +++ b/src/Features/Core/Portable/SolutionCrawler/AggregateIncrementalAnalyzer.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Options; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.SolutionCrawler { @@ -21,7 +22,7 @@ public AggregateIncrementalAnalyzer(Workspace workspace, IncrementalAnalyzerProv public async Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) { - foreach (var analyzer in this.Analyzers.Values) + foreach (var (_, analyzer) in this.Analyzers) { if (analyzer.IsValueCreated) { @@ -98,7 +99,7 @@ private bool TryGetAnalyzer(Project project, out IIncrementalAnalyzer analyzer) public void RemoveDocument(DocumentId documentId) { - foreach (var analyzer in this.Analyzers.Values) + foreach (var (_, analyzer) in this.Analyzers) { if (analyzer.IsValueCreated) { @@ -109,7 +110,7 @@ public void RemoveDocument(DocumentId documentId) public void RemoveProject(ProjectId projectId) { - foreach (var analyzer in this.Analyzers.Values) + foreach (var (_, analyzer) in this.Analyzers) { if (analyzer.IsValueCreated) { diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs index 95835a856156b..ee80c165b5996 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectState.cs @@ -133,7 +133,7 @@ private static async Task ComputeLatestDocumentVersionAsync(Immuta { // this may produce a version that is out of sync with the actual Document versions. var latestVersion = VersionStamp.Default; - foreach (var doc in documentStates.Values) + foreach (var (_, doc) in documentStates) { cancellationToken.ThrowIfCancellationRequested(); @@ -144,7 +144,7 @@ private static async Task ComputeLatestDocumentVersionAsync(Immuta } } - foreach (var additionalDoc in additionalDocumentStates.Values) + foreach (var (_, additionalDoc) in additionalDocumentStates) { cancellationToken.ThrowIfCancellationRequested(); @@ -180,7 +180,7 @@ private static async Task ComputeLatestDocumentTopLevelChangeVersi { // this may produce a version that is out of sync with the actual Document versions. var latestVersion = VersionStamp.Default; - foreach (var doc in documentStates.Values) + foreach (var (_, doc) in documentStates) { cancellationToken.ThrowIfCancellationRequested(); @@ -188,7 +188,7 @@ private static async Task ComputeLatestDocumentTopLevelChangeVersi latestVersion = version.GetNewerVersion(latestVersion); } - foreach (var additionalDoc in additionalDocumentStates.Values) + foreach (var (_, additionalDoc) in additionalDocumentStates) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs index e2929c3881bfc..a2f483256f9c4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionState.cs @@ -372,7 +372,7 @@ public ProjectState GetProjectState(IAssemblySymbol assemblySymbol, Cancellation } // TODO: Remove this loop when we add source assembly symbols to s_assemblyOrModuleSymbolToProjectMap - foreach (var state in _projectIdToProjectStateMap.Values) + foreach (var (_, state) in _projectIdToProjectStateMap) { if (this.TryGetCompilation(state.Id, out var compilation)) { From 4bf659862e000676b6986db224cce1c35708ccba Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 01:22:25 -0700 Subject: [PATCH 043/214] Get symbol-tree persistence working in OOP. --- .../AssemblySerializationInfoService.cs | 48 ++++---------- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 10 +-- .../SymbolTreeInfo_Serialization.cs | 50 +++++++------- .../SymbolTree/SymbolTreeInfo_Source.cs | 6 +- .../AssemblySerializationInfoService.cs | 66 +++++++++++++------ .../IAssemblySerializationInfoService.cs | 20 +++--- .../Remote/Core/RemoteWorkspaces.csproj | 1 + .../RemoteAssemblySerializationInfoService.cs | 12 ++++ 8 files changed, 114 insertions(+), 99 deletions(-) create mode 100644 src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs diff --git a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs b/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs index 77e84e7df16b9..0134ffb8712cc 100644 --- a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs +++ b/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs @@ -1,36 +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.Composition; -using System.IO; -using Microsoft.CodeAnalysis.Host.Mef; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Serialization -{ - [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Host)] - [Shared] - internal class AssemblySerializationInfoService : IAssemblySerializationInfoService - { - public bool Serializable(Solution solution, string assemblyFilePath) - { - if (assemblyFilePath == null || !File.Exists(assemblyFilePath)) - { - return false; - } - - // if solution is not from a disk, just create one. - if (solution.FilePath == null || !File.Exists(solution.FilePath)) - { - return false; - } - - return true; - } - - public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) - { - prefix = PathUtilities.GetRelativePath(solution.FilePath, assemblyFilePath); - 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.Composition; +//using Microsoft.CodeAnalysis.Host.Mef; + +//namespace Microsoft.CodeAnalysis.Serialization +//{ +// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Host), Shared] +// internal class VisualStudioAssemblySerializationInfoService : SimpleAssemblySerializationInfoService +// { +// } +//} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index e50d112fcb67c..559ab93bf7621 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -10,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Utilities; using Roslyn.Utilities; @@ -132,13 +133,8 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( CancellationToken cancellationToken) { var filePath = reference.FilePath; - var checksum = IOUtilities.PerformIO(() => - { - using (var stream = File.OpenRead(filePath)) - { - return Checksum.Create(stream); - } - }, null); + var serializer = new Serializer(solution.Workspace); + var checksum = serializer.CreateChecksum(reference, cancellationToken); return LoadOrCreateAsync( solution, diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index ef9622102956a..8ef672b0aeb32 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -85,7 +85,7 @@ private static async Task LoadOrCreateAsync( { // See if we can even use serialization. If not, we'll just have to make the value // from scratch. - if (checksum == null || ShouldCreateFromScratch(solution, filePath, out var prefix, cancellationToken)) + if (checksum == null) // || ShouldCreateFromScratch(solution, filePath, out var prefix, cancellationToken)) { return loadOnly ? null : create(); } @@ -97,7 +97,7 @@ private static async Task LoadOrCreateAsync( using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) { // Get the unique key to identify our data. - var key = PrefixMetadataSymbolTreeInfo + prefix + keySuffix; + var key = PrefixMetadataSymbolTreeInfo + keySuffix; using (var stream = await storage.ReadStreamAsync(key, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { @@ -142,33 +142,33 @@ private static async Task LoadOrCreateAsync( return result; } - private static bool ShouldCreateFromScratch( - Solution solution, - string filePath, - out string prefix, - CancellationToken cancellationToken) - { - prefix = null; + //private static bool ShouldCreateFromScratch( + // Solution solution, + // string filePath, + // out string prefix, + // CancellationToken cancellationToken) + //{ + // prefix = null; - var service = solution.Workspace.Services.GetService(); - if (service == null) - { - return true; - } + // var service = solution.Workspace.Services.GetService(); + // if (service == null) + // { + // return true; + // } - // check whether the assembly that belong to a solution is something we can serialize - if (!service.Serializable(solution, filePath)) - { - return true; - } + // // check whether the assembly that belong to a solution is something we can serialize + // if (!service.Serializable(solution, filePath)) + // { + // return true; + // } - if (!service.TryGetSerializationPrefix(solution, filePath, out prefix)) - { - return true; - } + // if (!service.TryGetSerializationPrefix(solution, filePath, out prefix)) + // { + // return true; + // } - return false; - } + // return false; + //} public void WriteTo(ObjectWriter writer) { diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index ef856d9962a5c..c0efdf7966a07 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Collections; +using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.FindSymbols @@ -28,7 +29,10 @@ public static async Task GetInfoForSourceAssemblyAsync( Project project, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var checksum = await project.State.GetChecksumAsync(cancellationToken).ConfigureAwait(false); + var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + + var checksum = Checksum.Create("SymbolTree", + new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions }); return await LoadOrCreateSourceSymbolTreeInfoAsync( project.Solution, compilation.Assembly, checksum, project.FilePath, diff --git a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs index 1122ce7629f62..bfd49fa4cd9f7 100644 --- a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs +++ b/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs @@ -1,23 +1,49 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.Host.Mef; +//using System.Composition; +//using System.IO; +//using Microsoft.CodeAnalysis.Host.Mef; +//using Roslyn.Utilities; -namespace Microsoft.CodeAnalysis.Serialization -{ - [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Default)] - [Shared] - internal class AssemblySerializationInfoService : IAssemblySerializationInfoService - { - public bool Serializable(Solution solution, string assemblyFilePath) - { - return false; - } +//namespace Microsoft.CodeAnalysis.Serialization +//{ +// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Default), Shared] +// internal class DefaultAssemblySerializationInfoService : IAssemblySerializationInfoService +// { +// public bool Serializable(Solution solution, string assemblyFilePath) +// { +// return false; +// } - public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) - { - prefix = string.Empty; - return false; - } - } -} +// public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) +// { +// prefix = string.Empty; +// return false; +// } +// } + +// internal class SimpleAssemblySerializationInfoService : IAssemblySerializationInfoService +// { +// public bool Serializable(Solution solution, string assemblyFilePath) +// { +// if (assemblyFilePath == null || !File.Exists(assemblyFilePath)) +// { +// return false; +// } + +// // if solution is not from a disk, just create one. +// if (solution.FilePath == null || !File.Exists(solution.FilePath)) +// { +// return false; +// } + +// return true; +// } + +// public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) +// { +// prefix = PathUtilities.GetRelativePath(solution.FilePath, assemblyFilePath); +// return true; +// } +// } +//} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs index 396d5f8c94086..897d9393db643 100644 --- a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs +++ b/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs @@ -1,12 +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. +//// 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.Host; +//using Microsoft.CodeAnalysis.Host; -namespace Microsoft.CodeAnalysis.Serialization -{ - internal interface IAssemblySerializationInfoService : IWorkspaceService - { - bool Serializable(Solution solution, string assemblyFilePath); - bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix); - } -} +//namespace Microsoft.CodeAnalysis.Serialization +//{ +// internal interface IAssemblySerializationInfoService : IWorkspaceService +// { +// bool Serializable(Solution solution, string assemblyFilePath); +// bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix); +// } +//} diff --git a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj index 7493d87d3a1cd..146bd2bcec61e 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj +++ b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj @@ -64,6 +64,7 @@ + diff --git a/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs b/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs new file mode 100644 index 0000000000000..cece2026f9f7a --- /dev/null +++ b/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs @@ -0,0 +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.Composition; +//using Microsoft.CodeAnalysis.Host.Mef; + +//namespace Microsoft.CodeAnalysis.Serialization +//{ +// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), layer: WorkspaceKind.RemoteWorkspace), Shared] +// internal class RemoteAssemblySerializationInfoService : SimpleAssemblySerializationInfoService +// { +// } +//} \ No newline at end of file From d684965b75417b54645612fff17a11ee23cfd0ab Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 01:32:25 -0700 Subject: [PATCH 044/214] Remove dead files. --- .../AssemblySerializationInfoService.cs | 12 ----- .../Core/Def/ServicesVisualStudio.csproj | 1 - .../AssemblySerializationInfoService.cs | 49 ------------------- .../IAssemblySerializationInfoService.cs | 12 ----- .../Core/Portable/Workspaces.csproj | 2 - .../Remote/Core/RemoteWorkspaces.csproj | 1 - .../RemoteAssemblySerializationInfoService.cs | 12 ----- 7 files changed, 89 deletions(-) delete mode 100644 src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs delete mode 100644 src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs delete mode 100644 src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs delete mode 100644 src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs diff --git a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs b/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs deleted file mode 100644 index 0134ffb8712cc..0000000000000 --- a/src/VisualStudio/Core/Def/Implementation/Serialization/AssemblySerializationInfoService.cs +++ /dev/null @@ -1,12 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.Host.Mef; - -//namespace Microsoft.CodeAnalysis.Serialization -//{ -// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Host), Shared] -// internal class VisualStudioAssemblySerializationInfoService : SimpleAssemblySerializationInfoService -// { -// } -//} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index e688d411d9220..2f881f23eac39 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -128,7 +128,6 @@ - diff --git a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs deleted file mode 100644 index bfd49fa4cd9f7..0000000000000 --- a/src/Workspaces/Core/Portable/Serialization/AssemblySerializationInfoService.cs +++ /dev/null @@ -1,49 +0,0 @@ -//// 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.Composition; -//using System.IO; -//using Microsoft.CodeAnalysis.Host.Mef; -//using Roslyn.Utilities; - -//namespace Microsoft.CodeAnalysis.Serialization -//{ -// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), ServiceLayer.Default), Shared] -// internal class DefaultAssemblySerializationInfoService : IAssemblySerializationInfoService -// { -// public bool Serializable(Solution solution, string assemblyFilePath) -// { -// return false; -// } - -// public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) -// { -// prefix = string.Empty; -// return false; -// } -// } - -// internal class SimpleAssemblySerializationInfoService : IAssemblySerializationInfoService -// { -// public bool Serializable(Solution solution, string assemblyFilePath) -// { -// if (assemblyFilePath == null || !File.Exists(assemblyFilePath)) -// { -// return false; -// } - -// // if solution is not from a disk, just create one. -// if (solution.FilePath == null || !File.Exists(solution.FilePath)) -// { -// return false; -// } - -// return true; -// } - -// public bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix) -// { -// prefix = PathUtilities.GetRelativePath(solution.FilePath, assemblyFilePath); -// return true; -// } -// } -//} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs b/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs deleted file mode 100644 index 897d9393db643..0000000000000 --- a/src/Workspaces/Core/Portable/Serialization/IAssemblySerializationInfoService.cs +++ /dev/null @@ -1,12 +0,0 @@ -//// 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.Host; - -//namespace Microsoft.CodeAnalysis.Serialization -//{ -// internal interface IAssemblySerializationInfoService : IWorkspaceService -// { -// bool Serializable(Solution solution, string assemblyFilePath); -// bool TryGetSerializationPrefix(Solution solution, string assemblyFilePath, out string prefix); -// } -//} diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 8387fcc21e295..b4d9a95803290 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -607,8 +607,6 @@ - - diff --git a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj index 146bd2bcec61e..7493d87d3a1cd 100644 --- a/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj +++ b/src/Workspaces/Remote/Core/RemoteWorkspaces.csproj @@ -64,7 +64,6 @@ - diff --git a/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs b/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs deleted file mode 100644 index cece2026f9f7a..0000000000000 --- a/src/Workspaces/Remote/Core/Services/RemoteAssemblySerializationInfoService.cs +++ /dev/null @@ -1,12 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.Host.Mef; - -//namespace Microsoft.CodeAnalysis.Serialization -//{ -// [ExportWorkspaceService(typeof(IAssemblySerializationInfoService), layer: WorkspaceKind.RemoteWorkspace), Shared] -// internal class RemoteAssemblySerializationInfoService : SimpleAssemblySerializationInfoService -// { -// } -//} \ No newline at end of file From 668a1a3cb5e6936c471ba28e7578e18e1a715d20 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 01:48:08 -0700 Subject: [PATCH 045/214] Store a single checksum for syntax-tree-indices. --- .../SymbolTreeInfo_Serialization.cs | 2 +- .../SymbolTree/SymbolTreeInfo_Source.cs | 2 +- .../FindSymbols/SyntaxTree/SyntaxTreeIndex.cs | 6 +-- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 5 +- .../SyntaxTree/SyntaxTreeIndex_Persistence.cs | 52 ++++++++----------- 5 files changed, 29 insertions(+), 38 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 8ef672b0aeb32..3313be4e8cb7b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal partial class SymbolTreeInfo { private const string PrefixMetadataSymbolTreeInfo = ""; - private const string SerializationFormat = "16"; + private const string SerializationFormat = "17"; /// /// Loads the SymbolTreeInfo for a given assembly symbol (metadata or project). If the diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index c0efdf7966a07..af967b5d419d0 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -31,7 +31,7 @@ public static async Task GetInfoForSourceAssemblyAsync( var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - var checksum = Checksum.Create("SymbolTree", + var checksum = Checksum.Create(nameof(SymbolTreeInfo), new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions }); return await LoadOrCreateSourceSymbolTreeInfoAsync( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs index e490eac19b709..057c589467c3d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs @@ -17,15 +17,13 @@ internal sealed partial class SyntaxTreeIndex private readonly DeclarationInfo _declarationInfo; private SyntaxTreeIndex( - Checksum textChecksum, - Checksum parseOptionsChecksum, + Checksum checksum, LiteralInfo literalInfo, IdentifierInfo identifierInfo, ContextInfo contextInfo, DeclarationInfo declarationInfo) { - TextChecksum = textChecksum; - ParseOptionsChecksum = parseOptionsChecksum; + this.Checksum = checksum; _literalInfo = literalInfo; _identifierInfo = identifierInfo; _contextInfo = contextInfo; diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index 7940bca7af26e..cc140a91d36e5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -154,11 +154,10 @@ private static async Task CreateInfoAsync(Document document, Ca } } - var checksums = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); return new SyntaxTreeIndex( - checksums.textChecksum, - checksums.parseOptionsChecksum, + checksum, new LiteralInfo( new BloomFilter(FalsePositiveProbability, stringLiterals, longLiterals)), new IdentifierInfo( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index dcc5651327367..a0f74cb4082b4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -13,42 +13,37 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal sealed partial class SyntaxTreeIndex : IObjectWritable { private const string PersistenceName = ""; - private const string SerializationFormat = "7"; + private const string SerializationFormat = "8"; - public readonly Checksum TextChecksum; - public readonly Checksum ParseOptionsChecksum; + public readonly Checksum Checksum; - private void WriteFormatAndChecksums(ObjectWriter writer, string formatVersion) + private void WriteFormatAndChecksum(ObjectWriter writer, string formatVersion) { writer.WriteString(formatVersion); - TextChecksum.WriteTo(writer); - ParseOptionsChecksum.WriteTo(writer); + Checksum.WriteTo(writer); } - private static bool TryReadFormatAndChecksums( - ObjectReader reader, string formatVersion, - out Checksum textChecksum, out Checksum parseOptionsChecksum) + private static bool TryReadFormatAndChecksum( + ObjectReader reader, string formatVersion, out Checksum checksum) { - textChecksum = null; - parseOptionsChecksum = null; + checksum = null; if (reader.ReadString() != formatVersion) { return false; } - textChecksum = Checksum.ReadFrom(reader); - parseOptionsChecksum = Checksum.ReadFrom(reader); + checksum = Checksum.ReadFrom(reader); return true; } private static async Task LoadAsync( Document document, string persistenceName, string formatVersion, - Func readFrom, CancellationToken cancellationToken) + Func readFrom, CancellationToken cancellationToken) { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); try { @@ -59,9 +54,9 @@ private static async Task LoadAsync( { if (reader != null) { - if (DataPreambleMatches(reader, formatVersion, textChecksum, parseOptionsChecksum)) + if (DataPreambleMatches(reader, formatVersion, checksum)) { - return readFrom(reader, textChecksum, parseOptionsChecksum); + return readFrom(reader, checksum); } } } @@ -75,14 +70,13 @@ private static async Task LoadAsync( } private static bool DataPreambleMatches( - ObjectReader reader, string formatVersion, Checksum textChecksum, Checksum parseOptionsChecksum) + ObjectReader reader, string formatVersion, Checksum checksum) { - return TryReadFormatAndChecksums(reader, formatVersion, out var persistTextChecksum, out var persistParseOptionsChecksum) && - persistTextChecksum == textChecksum && - persistParseOptionsChecksum == parseOptionsChecksum; + return TryReadFormatAndChecksum(reader, formatVersion, out var persistChecksum) && + persistChecksum == checksum; } - public static async Task<(Checksum textChecksum, Checksum parseOptionsChecksum)> GetChecksumsAsync( + public static async Task GetChecksumAsync( Document document, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); @@ -94,7 +88,7 @@ private static bool DataPreambleMatches( var parseOptionsChecksum = ChecksumCache.GetOrCreate( parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); - return (textChecksum, parseOptionsChecksum); + return Checksum.Create(nameof(SyntaxTreeIndex), new[] { textChecksum, parseOptionsChecksum }); } private static async Task SaveAsync( @@ -102,7 +96,7 @@ private static async Task SaveAsync( { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); try { @@ -110,7 +104,7 @@ private static async Task SaveAsync( using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - data.WriteFormatAndChecksums(writer, formatVersion); + data.WriteFormatAndChecksum(writer, formatVersion); data.WriteTo(writer); stream.Position = 0; @@ -130,7 +124,7 @@ private static async Task PrecalculatedAsync( { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var (textChecksum, parseOptionsChecksum) = await GetChecksumsAsync(document, cancellationToken).ConfigureAwait(false); + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); // check whether we already have info for this document try @@ -141,7 +135,7 @@ private static async Task PrecalculatedAsync( { if (reader != null) { - return DataPreambleMatches(reader, formatVersion, textChecksum, parseOptionsChecksum); + return DataPreambleMatches(reader, formatVersion, checksum); } } } @@ -162,7 +156,7 @@ public void WriteTo(ObjectWriter writer) } private static SyntaxTreeIndex ReadFrom( - ObjectReader reader, Checksum textChecksum, Checksum parseOptionsChecksum) + ObjectReader reader, Checksum checksum) { var literalInfo = LiteralInfo.TryReadFrom(reader); var identifierInfo = IdentifierInfo.TryReadFrom(reader); @@ -175,7 +169,7 @@ private static SyntaxTreeIndex ReadFrom( } return new SyntaxTreeIndex( - textChecksum, parseOptionsChecksum, literalInfo.Value, identifierInfo.Value, contextInfo.Value, declarationInfo.Value); + checksum, literalInfo.Value, identifierInfo.Value, contextInfo.Value, declarationInfo.Value); } private Task SaveAsync(Document document, CancellationToken cancellationToken) From c1d98abb92b3b5e240e6e4e8806c277b3042fdaa Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 10:56:29 -0700 Subject: [PATCH 046/214] Include filepath in key. --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 3313be4e8cb7b..8484f61fdac31 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -97,7 +97,7 @@ private static async Task LoadOrCreateAsync( using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) { // Get the unique key to identify our data. - var key = PrefixMetadataSymbolTreeInfo + keySuffix; + var key = PrefixMetadataSymbolTreeInfo + keySuffix + "_" + filePath; using (var stream = await storage.ReadStreamAsync(key, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { From b258ce445ef3705529ef804994b3165364a170cb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:13:16 -0700 Subject: [PATCH 047/214] Simplify checksum handling --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs | 2 +- .../SymbolTree/SymbolTreeInfo_Serialization.cs | 11 ++++++----- src/Workspaces/CoreTest/FindAllDeclarationsTests.cs | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 559ab93bf7621..4800832867a22 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -144,7 +144,7 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken), keySuffix: "_Metadata", getPersistedChecksum: info => info._checksum, - readObject: reader => ReadSymbolTreeInfo(reader, (version, names, nodes) => GetSpellCheckerTask(solution, version, filePath, names, nodes)), + readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 8484f61fdac31..5b135326ffae0 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -39,7 +39,7 @@ private static Task LoadOrCreateSourceSymbolTreeInfoAsync( create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken), keySuffix: "_Source", getPersistedChecksum: info => info._checksum, - readObject: reader => ReadSymbolTreeInfo(reader, (c, names, nodes) => GetSpellCheckerTask(solution, c, filePath, names, nodes)), + readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } @@ -198,16 +198,17 @@ public void WriteTo(ObjectWriter writer) } } - internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly(ObjectReader reader) + internal static SymbolTreeInfo ReadSymbolTreeInfo_ForTestingPurposesOnly( + ObjectReader reader, Checksum checksum) { return ReadSymbolTreeInfo(reader, - (checksum, names, nodes) => Task.FromResult( + (names, nodes) => Task.FromResult( new SpellChecker(checksum, nodes.Select(n => new StringSlice(names, n.NameSpan))))); } private static SymbolTreeInfo ReadSymbolTreeInfo( ObjectReader reader, - Func> createSpellCheckerTask) + Func> createSpellCheckerTask) { try { @@ -243,7 +244,7 @@ private static SymbolTreeInfo ReadSymbolTreeInfo( } } - var spellCheckerTask = createSpellCheckerTask(checksum, concatenatedNames, nodes); + var spellCheckerTask = createSpellCheckerTask(concatenatedNames, nodes); return new SymbolTreeInfo(checksum, concatenatedNames, nodes, spellCheckerTask, inheritanceMap); } } diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs index cf1ca2cb38a4b..193edcce5cada 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs @@ -555,7 +555,8 @@ public async Task TestSymbolTreeInfoSerialization() using (var readerStream = new MemoryStream(writerStream.ToArray())) using (var reader = ObjectReader.TryGetReader(readerStream)) { - var readInfo = SymbolTreeInfo.ReadSymbolTreeInfo_ForTestingPurposesOnly(reader); + var readInfo = SymbolTreeInfo.ReadSymbolTreeInfo_ForTestingPurposesOnly( + reader, Checksum.Null); info.AssertEquivalentTo(readInfo); } From cb4227cbcd698376e0f84825b9ea417e38def88c Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:14:31 -0700 Subject: [PATCH 048/214] Delete dead code. --- .../SymbolTreeInfo_Serialization.cs | 32 +------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 5b135326ffae0..7aa9bb7d1bfc5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -83,9 +83,7 @@ private static async Task LoadOrCreateAsync( Action writeObject, CancellationToken cancellationToken) where T : class { - // See if we can even use serialization. If not, we'll just have to make the value - // from scratch. - if (checksum == null) // || ShouldCreateFromScratch(solution, filePath, out var prefix, cancellationToken)) + if (checksum == null) { return loadOnly ? null : create(); } @@ -142,34 +140,6 @@ private static async Task LoadOrCreateAsync( return result; } - //private static bool ShouldCreateFromScratch( - // Solution solution, - // string filePath, - // out string prefix, - // CancellationToken cancellationToken) - //{ - // prefix = null; - - // var service = solution.Workspace.Services.GetService(); - // if (service == null) - // { - // return true; - // } - - // // check whether the assembly that belong to a solution is something we can serialize - // if (!service.Serializable(solution, filePath)) - // { - // return true; - // } - - // if (!service.TryGetSerializationPrefix(solution, filePath, out prefix)) - // { - // return true; - // } - - // return false; - //} - public void WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); From 3d7a2e95fe6841ef745be521dbd01f7424feba52 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:18:21 -0700 Subject: [PATCH 049/214] Rename method. --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index a0f74cb4082b4..056a8dba12453 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -54,7 +54,7 @@ private static async Task LoadAsync( { if (reader != null) { - if (DataPreambleMatches(reader, formatVersion, checksum)) + if (FormatAndChecksumMatches(reader, formatVersion, checksum)) { return readFrom(reader, checksum); } @@ -69,7 +69,7 @@ private static async Task LoadAsync( return null; } - private static bool DataPreambleMatches( + private static bool FormatAndChecksumMatches( ObjectReader reader, string formatVersion, Checksum checksum) { return TryReadFormatAndChecksum(reader, formatVersion, out var persistChecksum) && @@ -135,7 +135,7 @@ private static async Task PrecalculatedAsync( { if (reader != null) { - return DataPreambleMatches(reader, formatVersion, checksum); + return FormatAndChecksumMatches(reader, formatVersion, checksum); } } } From 0b69812cab528b02c4443494e08fa19bc1dcf249 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:20:21 -0700 Subject: [PATCH 050/214] Add comment. --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 056a8dba12453..6a2a5a81ad97b 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -79,6 +79,11 @@ private static bool FormatAndChecksumMatches( public static async Task GetChecksumAsync( Document document, CancellationToken cancellationToken) { + // Since we build the SyntaxTreeIndex from a SyntaxTree, we need our checksum to change + // any time the SyntaxTree could have changed. Right now, that can only happen if the + // text of the document changes, or the ParseOptions change. So we get the checksums + // for both of those, and merge them together to make the final checksum. + var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var textChecksum = Checksum.Create(WellKnownSynchronizationKinds.SourceText, text.GetChecksum()); From a046f9d8b96a4a67898ceeaede7fd645e641fdfe Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:21:53 -0700 Subject: [PATCH 051/214] Add comment. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 4800832867a22..991a752933fdc 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -133,6 +133,9 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( CancellationToken cancellationToken) { var filePath = reference.FilePath; + + // We can reuse the index for any given reference as long as it hasn't changed. + // So our checksum is just the checksum for the PEReference itself. var serializer = new Serializer(solution.Workspace); var checksum = serializer.CreateChecksum(reference, cancellationToken); From bea6d3afaad8e5c683f631664e29b4555fa8686e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:24:26 -0700 Subject: [PATCH 052/214] Add comment. --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index af967b5d419d0..c6fed0a8d6b31 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -29,8 +29,14 @@ public static async Task GetInfoForSourceAssemblyAsync( Project project, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + // The SymbolTree for source is built from the source-symbols from the project's compilation's + // assembly. Specifically, we only get the name, kind and parent/child relationship of all the + // child symbols. So we want to be able to reuse the index as long as none of these have + // changed. The only thing that can make those source-symbols change in that manner are if + // the text of any document changes, or if options for the project change. So we build our + // checksum out of that data. + var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var checksum = Checksum.Create(nameof(SymbolTreeInfo), new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions }); From ac513cd64e6d40fd9a916511eb925fc908f0eac3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:40:26 -0700 Subject: [PATCH 053/214] Simplify index handling. --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex.cs | 71 +++++++++++-------- .../SyntaxTree/SyntaxTreeIndex_Create.cs | 5 +- .../SyntaxTree/SyntaxTreeIndex_Persistence.cs | 40 ++++------- 3 files changed, 58 insertions(+), 58 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs index 057c589467c3d..592a9a94d1325 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex.cs @@ -30,59 +30,74 @@ private SyntaxTreeIndex( _declarationInfo = declarationInfo; } - /// - /// snapshot based cache to guarantee same info is returned without re-calculating for - /// same solution snapshot. - /// - /// since document will be re-created per new solution, this should go away as soon as - /// there is any change on workspace. - /// - private static readonly ConditionalWeakTable s_infoCache - = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_documentToIndex = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_documentIdToIndex = new ConditionalWeakTable(); public static async Task PrecalculateAsync(Document document, CancellationToken cancellationToken) { Contract.Requires(document.IsFromPrimaryBranch()); - // we already have information. move on - if (await PrecalculatedAsync(document, cancellationToken).ConfigureAwait(false)) + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); + + // Check if we've already created and persisted the index for this document. + if (await PrecalculatedAsync(document, checksum, cancellationToken).ConfigureAwait(false)) { return; } - var data = await CreateInfoAsync(document, cancellationToken).ConfigureAwait(false); + // If not, create and save the index. + var data = await CreateIndexAsync(document, checksum, cancellationToken).ConfigureAwait(false); await data.SaveAsync(document, cancellationToken).ConfigureAwait(false); } - private static async Task GetIndexAsync( + public static async Task GetIndexAsync( + Document document, + CancellationToken cancellationToken) + { + // See if we already cached an index with this direct document index. If so we can just + // return it with no additional work. + if (!s_documentToIndex.TryGetValue(document, out var index)) + { + index = await GetIndexWorkerAsync(document, cancellationToken).ConfigureAwait(false); + + // Populate our caches with this data. + s_documentToIndex.GetValue(document, _ => index); + s_documentIdToIndex.GetValue(document.Id, _ => index); + } + + return index; + } + + private static async Task GetIndexWorkerAsync( Document document, - ConditionalWeakTable cache, - Func> generator, CancellationToken cancellationToken) { - if (cache.TryGetValue(document, out var info)) + var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); + + // Check if we have an index for a previous version of this document. If our + // checksums match, we can just use that. + if (s_documentIdToIndex.TryGetValue(document.Id, out var index) && + index.Checksum == checksum) { - return info; + // The previous index we stored with this documentId is still valid. Just + // return that. + return index; } - info = await generator(document, cancellationToken).ConfigureAwait(false); - if (info != null) + // What we have in memory isn't valid. Try to load from the persistence service. + index = await LoadAsync(document, checksum, cancellationToken).ConfigureAwait(false); + if (index != null) { - return cache.GetValue(document, _ => info); + return index; } // alright, we don't have cached information, re-calculate them here. - var data = await CreateInfoAsync(document, cancellationToken).ConfigureAwait(false); + index = await CreateIndexAsync(document, checksum, cancellationToken).ConfigureAwait(false); // okay, persist this info - await data.SaveAsync(document, cancellationToken).ConfigureAwait(false); + await index.SaveAsync(document, cancellationToken).ConfigureAwait(false); - return cache.GetValue(document, _ => data); + return index; } - - public static Task GetIndexAsync(Document document, CancellationToken cancellationToken) - => GetIndexAsync(document, s_infoCache, s_loadAsync, cancellationToken); - - private static Func> s_loadAsync = LoadAsync; } } \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs index cc140a91d36e5..1632f9d07d4d2 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Create.cs @@ -23,7 +23,8 @@ internal sealed partial class SyntaxTreeIndex public static readonly ObjectPool> LongLiteralHashSetPool = new ObjectPool>(() => new HashSet(), 20); - private static async Task CreateInfoAsync(Document document, CancellationToken cancellationToken) + private static async Task CreateIndexAsync( + Document document, Checksum checksum, CancellationToken cancellationToken) { var syntaxFacts = document.GetLanguageService(); var ignoreCase = syntaxFacts != null && !syntaxFacts.IsCaseSensitive; @@ -154,8 +155,6 @@ private static async Task CreateInfoAsync(Document document, Ca } } - var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); - return new SyntaxTreeIndex( checksum, new LiteralInfo( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 6a2a5a81ad97b..e141a9ae8974d 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols internal sealed partial class SyntaxTreeIndex : IObjectWritable { private const string PersistenceName = ""; - private const string SerializationFormat = "8"; + private const string SerializationFormat = "9"; public readonly Checksum Checksum; @@ -37,26 +37,23 @@ private static bool TryReadFormatAndChecksum( } private static async Task LoadAsync( - Document document, string persistenceName, string formatVersion, - Func readFrom, CancellationToken cancellationToken) + Document document, Checksum checksum, CancellationToken cancellationToken) { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); - try { // attempt to load from persisted state using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) - using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false)) + using (var stream = await storage.ReadStreamAsync(document, PersistenceName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { if (reader != null) { - if (FormatAndChecksumMatches(reader, formatVersion, checksum)) + if (FormatAndChecksumMatches(reader, SerializationFormat, checksum)) { - return readFrom(reader, checksum); + return ReadFrom(reader, checksum); } } } @@ -96,12 +93,11 @@ public static async Task GetChecksumAsync( return Checksum.Create(nameof(SyntaxTreeIndex), new[] { textChecksum, parseOptionsChecksum }); } - private static async Task SaveAsync( - Document document, string persistenceName, string formatVersion, SyntaxTreeIndex data, CancellationToken cancellationToken) + private async Task SaveAsync( + Document document, CancellationToken cancellationToken) { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); try { @@ -109,11 +105,11 @@ private static async Task SaveAsync( using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - data.WriteFormatAndChecksum(writer, formatVersion); - data.WriteTo(writer); + this.WriteFormatAndChecksum(writer, SerializationFormat); + this.WriteTo(writer); stream.Position = 0; - return await storage.WriteStreamAsync(document, persistenceName, stream, cancellationToken).ConfigureAwait(false); + return await storage.WriteStreamAsync(document, PersistenceName, stream, cancellationToken).ConfigureAwait(false); } } catch (Exception e) when (IOUtilities.IsNormalIOException(e)) @@ -125,22 +121,21 @@ private static async Task SaveAsync( } private static async Task PrecalculatedAsync( - Document document, string persistenceName, string formatVersion, CancellationToken cancellationToken) + Document document, Checksum checksum, CancellationToken cancellationToken) { var solution = document.Project.Solution; var persistentStorageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - var checksum = await GetChecksumAsync(document, cancellationToken).ConfigureAwait(false); // check whether we already have info for this document try { using (var storage = persistentStorageService.GetStorage(solution, checkBranchId: false)) - using (var stream = await storage.ReadStreamAsync(document, persistenceName, cancellationToken).ConfigureAwait(false)) + using (var stream = await storage.ReadStreamAsync(document, PersistenceName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream)) { if (reader != null) { - return FormatAndChecksumMatches(reader, formatVersion, checksum); + return FormatAndChecksumMatches(reader, SerializationFormat, checksum); } } } @@ -176,14 +171,5 @@ private static SyntaxTreeIndex ReadFrom( return new SyntaxTreeIndex( checksum, literalInfo.Value, identifierInfo.Value, contextInfo.Value, declarationInfo.Value); } - - private Task SaveAsync(Document document, CancellationToken cancellationToken) - => SaveAsync(document, PersistenceName, SerializationFormat, this, cancellationToken); - - private static Task LoadAsync(Document document, CancellationToken cancellationToken) - => LoadAsync(document, PersistenceName, SerializationFormat, ReadFrom, cancellationToken); - - private static Task PrecalculatedAsync(Document document, CancellationToken cancellationToken) - => PrecalculatedAsync(document, PersistenceName, SerializationFormat, cancellationToken); } } \ No newline at end of file From 94f0334ff6154ce6a9360c15aa1e92aa589e80a3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 11:47:31 -0700 Subject: [PATCH 054/214] Simplify serialization for SymbolTreeInfo --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs | 1 - .../SymbolTree/SymbolTreeInfo_Serialization.cs | 11 ++++------- .../Core/Portable/Utilities/SpellChecker.cs | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 991a752933fdc..8c7830b0b610e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -148,7 +148,6 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( keySuffix: "_Metadata", getPersistedChecksum: info => info._checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), - writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 7aa9bb7d1bfc5..20bc208548399 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols { - internal partial class SymbolTreeInfo + internal partial class SymbolTreeInfo : IObjectWritable { private const string PrefixMetadataSymbolTreeInfo = ""; private const string SerializationFormat = "17"; @@ -40,7 +40,6 @@ private static Task LoadOrCreateSourceSymbolTreeInfoAsync( keySuffix: "_Source", getPersistedChecksum: info => info._checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), - writeObject: (w, i) => i.WriteTo(w), cancellationToken: cancellationToken); } @@ -63,7 +62,6 @@ private static Task LoadOrCreateSpellCheckerAsync( keySuffix: "_SpellChecker", getPersistedChecksum: s => s.Checksum, readObject: SpellChecker.ReadFrom, - writeObject: (w, i) => i.WriteTo(w), cancellationToken: CancellationToken.None); } @@ -80,8 +78,7 @@ private static async Task LoadOrCreateAsync( string keySuffix, Func getPersistedChecksum, Func readObject, - Action writeObject, - CancellationToken cancellationToken) where T : class + CancellationToken cancellationToken) where T : class, IObjectWritable { if (checksum == null) { @@ -129,7 +126,7 @@ private static async Task LoadOrCreateAsync( using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - writeObject(writer, result); + result.WriteTo(writer); stream.Position = 0; await storage.WriteStreamAsync(key, stream, cancellationToken).ConfigureAwait(false); @@ -140,7 +137,7 @@ private static async Task LoadOrCreateAsync( return result; } - public void WriteTo(ObjectWriter writer) + void IObjectWritable.WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); _checksum.WriteTo(writer); diff --git a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs index 77666df302cef..31035b894eba2 100644 --- a/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs +++ b/src/Workspaces/Core/Portable/Utilities/SpellChecker.cs @@ -10,7 +10,7 @@ namespace Roslyn.Utilities { - internal class SpellChecker + internal class SpellChecker : IObjectWritable { private const string SerializationFormat = "3"; @@ -42,7 +42,7 @@ public IList FindSimilarWords(string value, bool substringsAreSimilar) return array; } - internal void WriteTo(ObjectWriter writer) + void IObjectWritable.WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); Checksum.WriteTo(writer); From 264ea45b59aacb8168b2060bd99fb4c2865d0ba3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:09:52 -0700 Subject: [PATCH 055/214] Move to checksums for the incremental analyzer. --- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 86 ++++++++----------- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 23 +++-- .../SymbolTreeInfo_Serialization.cs | 2 +- .../SymbolTree/SymbolTreeInfo_Source.cs | 14 ++- 4 files changed, 64 insertions(+), 61 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 673afccdea765..122e5218fdd11 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -40,19 +40,19 @@ internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerP { private struct ProjectInfo { - public readonly VersionStamp VersionStamp; + public readonly Checksum Checksum; public readonly SymbolTreeInfo SymbolTreeInfo; - public ProjectInfo(VersionStamp versionStamp, SymbolTreeInfo info) + public ProjectInfo(Checksum checksum, SymbolTreeInfo info) { - VersionStamp = versionStamp; + Checksum = checksum; SymbolTreeInfo = info; } } private struct MetadataInfo { - public readonly DateTime TimeStamp; + public readonly Checksum Checksum; /// /// Note: can be null if were unable to create a SymbolTreeInfo @@ -67,9 +67,9 @@ private struct MetadataInfo /// public readonly HashSet ReferencingProjects; - public MetadataInfo(DateTime timeStamp, SymbolTreeInfo info, HashSet referencingProjects) + public MetadataInfo(Checksum checksum, SymbolTreeInfo info, HashSet referencingProjects) { - TimeStamp = timeStamp; + Checksum = checksum; SymbolTreeInfo = info; ReferencingProjects = referencingProjects; } @@ -102,28 +102,24 @@ private void OnCacheFlushRequested(object sender, EventArgs e) } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - { - return new SymbolTreeInfoCacheService(_projectToInfo, _metadataPathToInfo); - } + => new SymbolTreeInfoCacheService(_projectToInfo, _metadataPathToInfo); private static string GetReferenceKey(PortableExecutableReference reference) - { - return reference.FilePath ?? reference.Display; - } + => reference.FilePath ?? reference.Display; - private static bool TryGetLastWriteTime(string path, out DateTime time) + private static async Task GetTotalProjectChecksumAsync(Project project, CancellationToken cancellationToken) { - var succeeded = false; - time = IOUtilities.PerformIO( - () => - { - var result = File.GetLastWriteTimeUtc(path); - succeeded = true; - return result; - }, - default(DateTime)); + // We want to recompute the symbol trees for a project whenever its source symbols + // change, or if it's metadata references change. So we get the checksums for both + // and we produce a final checksum out of that. + var projectSourceSymbolsChecksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync( + project, cancellationToken).ConfigureAwait(false); + + var projectStateChecksum = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var metadataReferencesChecksum = projectStateChecksum.MetadataReferences.Checksum; - return succeeded; + return Checksum.Create(nameof(SymbolTreeInfoIncrementalAnalyzerProvider), + new[] { projectSourceSymbolsChecksum, metadataReferencesChecksum }); } private class SymbolTreeInfoCacheService : ISymbolTreeInfoCacheService @@ -144,15 +140,15 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( PortableExecutableReference reference, CancellationToken cancellationToken) { + var checksum = SymbolTreeInfo.GetMetadataChecksum(solution, reference, cancellationToken); + var key = GetReferenceKey(reference); if (key != null) { - if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo)) + if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && + metadataInfo.Checksum == checksum) { - if (TryGetLastWriteTime(key, out var writeTime) && writeTime == metadataInfo.TimeStamp) - { - return metadataInfo.SymbolTreeInfo; - } + return metadataInfo.SymbolTreeInfo; } } @@ -160,20 +156,17 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( // Note: pass 'loadOnly' so we only attempt to load from disk, not to actually // try to create the metadata. var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( - solution, reference, loadOnly: true, cancellationToken: cancellationToken).ConfigureAwait(false); + solution, reference, checksum, loadOnly: true, cancellationToken: cancellationToken).ConfigureAwait(false); return info; } public async Task TryGetSourceSymbolTreeInfoAsync( Project project, CancellationToken cancellationToken) { - if (_projectToInfo.TryGetValue(project.Id, out var projectInfo)) + if (_projectToInfo.TryGetValue(project.Id, out var projectInfo) && + projectInfo.Checksum == await GetTotalProjectChecksumAsync(project, cancellationToken).ConfigureAwait(false)) { - var version = await project.GetSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - if (version == projectInfo.VersionStamp) - { - return projectInfo.SymbolTreeInfo; - } + return projectInfo.SymbolTreeInfo; } return null; @@ -236,8 +229,9 @@ private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken // if any of the source files changed, or if the project version itself changed. // (The latter happens when something happens to the project like metadata // changing on disk). - var version = await project.GetSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - if (!_projectToInfo.TryGetValue(project.Id, out var projectInfo) || projectInfo.VersionStamp != version) + var checksum = await GetTotalProjectChecksumAsync(project, cancellationToken).ConfigureAwait(false); + if (!_projectToInfo.TryGetValue(project.Id, out var projectInfo) || + projectInfo.Checksum != checksum) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); @@ -249,7 +243,7 @@ private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. - projectInfo = new ProjectInfo(version, await projectTask.ConfigureAwait(false)); + projectInfo = new ProjectInfo(checksum, await projectTask.ConfigureAwait(false)); _projectToInfo.AddOrUpdate(project.Id, projectInfo, (_1, _2) => projectInfo); } } @@ -273,21 +267,17 @@ private async Task UpdateReferenceAsync( return; } - if (!TryGetLastWriteTime(key, out var lastWriteTime)) - { - // Couldn't get the write time. Just ignore this reference. - return; - } - - if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || metadataInfo.TimeStamp == lastWriteTime) + var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); + if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || + metadataInfo.Checksum != checksum) { var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( - project.Solution, reference, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. - metadataInfo = new MetadataInfo(lastWriteTime, info, metadataInfo.ReferencingProjects ?? new HashSet()); + metadataInfo = new MetadataInfo(checksum, info, metadataInfo.ReferencingProjects ?? new HashSet()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } @@ -318,4 +308,4 @@ private void RemoveMetadataReferences(ProjectId projectId) } } } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 8c7830b0b610e..7b32419b0fb08 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -67,6 +67,7 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference public static Task TryGetInfoForMetadataReferenceAsync( Solution solution, PortableExecutableReference reference, + Checksum checksum, bool loadOnly, CancellationToken cancellationToken) { @@ -92,11 +93,11 @@ public static Task TryGetInfoForMetadataReferenceAsync( } return TryGetInfoForMetadataReferenceSlowAsync( - solution, reference, loadOnly, metadata, cancellationToken); + solution, reference, checksum, loadOnly, metadata, cancellationToken); } private static async Task TryGetInfoForMetadataReferenceSlowAsync( - Solution solution, PortableExecutableReference reference, + Solution solution, PortableExecutableReference reference, Checksum checksum, bool loadOnly, Metadata metadata, CancellationToken cancellationToken) { // Find the lock associated with this piece of metadata. This way only one thread is @@ -111,7 +112,7 @@ private static async Task TryGetInfoForMetadataReferenceSlowAsyn } var info = await LoadOrCreateMetadataSymbolTreeInfoAsync( - solution, reference, loadOnly, cancellationToken: cancellationToken).ConfigureAwait(false); + solution, reference, checksum, loadOnly, cancellationToken).ConfigureAwait(false); if (info == null && loadOnly) { return null; @@ -126,19 +127,25 @@ private static async Task TryGetInfoForMetadataReferenceSlowAsyn } } + public static Checksum GetMetadataChecksum( + Solution solution, PortableExecutableReference reference, CancellationToken cancellationToken) + { + // We can reuse the index for any given reference as long as it hasn't changed. + // So our checksum is just the checksum for the PEReference itself. + var serializer = new Serializer(solution.Workspace); + var checksum = serializer.CreateChecksum(reference, cancellationToken); + return checksum; + } + private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( Solution solution, PortableExecutableReference reference, + Checksum checksum, bool loadOnly, CancellationToken cancellationToken) { var filePath = reference.FilePath; - // We can reuse the index for any given reference as long as it hasn't changed. - // So our checksum is just the checksum for the PEReference itself. - var serializer = new Serializer(solution.Workspace); - var checksum = serializer.CreateChecksum(reference, cancellationToken); - return LoadOrCreateAsync( solution, checksum, diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index 20bc208548399..c5a6d2fed76bf 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -137,7 +137,7 @@ private static async Task LoadOrCreateAsync( return result; } - void IObjectWritable.WriteTo(ObjectWriter writer) + public void WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); _checksum.WriteTo(writer); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index c6fed0a8d6b31..a962d30541213 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -30,6 +30,15 @@ public static async Task GetInfoForSourceAssemblyAsync( { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var checksum = await GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false); + + return await LoadOrCreateSourceSymbolTreeInfoAsync( + project.Solution, compilation.Assembly, checksum, project.FilePath, + loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + public static async Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) + { // The SymbolTree for source is built from the source-symbols from the project's compilation's // assembly. Specifically, we only get the name, kind and parent/child relationship of all the // child symbols. So we want to be able to reuse the index as long as none of these have @@ -39,10 +48,7 @@ public static async Task GetInfoForSourceAssemblyAsync( var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var checksum = Checksum.Create(nameof(SymbolTreeInfo), new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions }); - - return await LoadOrCreateSourceSymbolTreeInfoAsync( - project.Solution, compilation.Assembly, checksum, project.FilePath, - loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + return checksum; } internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( From b53eb293e48b2de08d7da2d67e91a9826abf6a14 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:18:35 -0700 Subject: [PATCH 056/214] Simplify incremental analyzer more. --- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 49 +++++++------------ .../SymbolTree/SymbolTreeInfo_Metadata.cs | 10 ++++ .../SymbolTree/SymbolTreeInfo_Source.cs | 4 +- 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 122e5218fdd11..9bee5bf9d2814 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -107,21 +107,6 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) private static string GetReferenceKey(PortableExecutableReference reference) => reference.FilePath ?? reference.Display; - private static async Task GetTotalProjectChecksumAsync(Project project, CancellationToken cancellationToken) - { - // We want to recompute the symbol trees for a project whenever its source symbols - // change, or if it's metadata references change. So we get the checksums for both - // and we produce a final checksum out of that. - var projectSourceSymbolsChecksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync( - project, cancellationToken).ConfigureAwait(false); - - var projectStateChecksum = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - var metadataReferencesChecksum = projectStateChecksum.MetadataReferences.Checksum; - - return Checksum.Create(nameof(SymbolTreeInfoIncrementalAnalyzerProvider), - new[] { projectSourceSymbolsChecksum, metadataReferencesChecksum }); - } - private class SymbolTreeInfoCacheService : ISymbolTreeInfoCacheService { private readonly ConcurrentDictionary _projectToInfo; @@ -164,7 +149,7 @@ public async Task TryGetSourceSymbolTreeInfoAsync( Project project, CancellationToken cancellationToken) { if (_projectToInfo.TryGetValue(project.Id, out var projectInfo) && - projectInfo.Checksum == await GetTotalProjectChecksumAsync(project, cancellationToken).ConfigureAwait(false)) + projectInfo.Checksum == await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false)) { return projectInfo.SymbolTreeInfo; } @@ -225,41 +210,41 @@ private async Task UpdateSymbolTreeInfoAsync(Project project, CancellationToken return; } - // Check the semantic version of this project. The semantic version will change - // if any of the source files changed, or if the project version itself changed. - // (The latter happens when something happens to the project like metadata - // changing on disk). - var checksum = await GetTotalProjectChecksumAsync(project, cancellationToken).ConfigureAwait(false); + // Produce the indices for the source and metadata symbols in parallel. + var projectTask = UpdateSourceSymbolTreeInfoAsync(project, cancellationToken); + var referencesTask = UpdateReferencesAync(project, cancellationToken); + + await Task.WhenAll(projectTask, referencesTask).ConfigureAwait(false); + } + + private async Task UpdateSourceSymbolTreeInfoAsync(Project project, CancellationToken cancellationToken) + { + var checksum = await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false); if (!_projectToInfo.TryGetValue(project.Id, out var projectInfo) || projectInfo.Checksum != checksum) { - var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - - // Update the symbol tree infos for metadata and source in parallel. - var referencesTask = UpdateReferencesAync(project, compilation, cancellationToken); - var projectTask = SymbolTreeInfo.GetInfoForSourceAssemblyAsync(project, cancellationToken); - - await Task.WhenAll(referencesTask, projectTask).ConfigureAwait(false); + var info = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( + project, checksum, cancellationToken).ConfigureAwait(false); // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. - projectInfo = new ProjectInfo(checksum, await projectTask.ConfigureAwait(false)); + projectInfo = new ProjectInfo(checksum, info); _projectToInfo.AddOrUpdate(project.Id, projectInfo, (_1, _2) => projectInfo); } } - private Task UpdateReferencesAync(Project project, Compilation compilation, CancellationToken cancellationToken) + private Task UpdateReferencesAync(Project project, CancellationToken cancellationToken) { // Process all metadata references in parallel. var tasks = project.MetadataReferences.OfType() - .Select(r => UpdateReferenceAsync(project, compilation, r, cancellationToken)) + .Select(r => UpdateReferenceAsync(project, r, cancellationToken)) .ToArray(); return Task.WhenAll(tasks); } private async Task UpdateReferenceAsync( - Project project, Compilation compilation, PortableExecutableReference reference, CancellationToken cancellationToken) + Project project, PortableExecutableReference reference, CancellationToken cancellationToken) { var key = GetReferenceKey(reference); if (key == null) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 7b32419b0fb08..f3c19ad5a6f1a 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -59,6 +59,16 @@ private static Metadata GetMetadataNoThrow(PortableExecutableReference reference } } + public static Task TryGetInfoForMetadataReferenceAsync( + Solution solution, PortableExecutableReference reference, + bool loadOnly, CancellationToken cancellationToken) + { + var checksum = GetMetadataChecksum(solution, reference, cancellationToken); + return TryGetInfoForMetadataReferenceAsync( + solution, reference, checksum, + loadOnly, cancellationToken); + } + /// /// Produces a for a given . /// Note: can return null if we weren't able to actually load the metadata for some diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index a962d30541213..1ec7fd1ac8961 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -26,12 +26,10 @@ private static void FreeSymbolMap(MultiDictionary symbolMap) } public static async Task GetInfoForSourceAssemblyAsync( - Project project, CancellationToken cancellationToken) + Project project, Checksum checksum, CancellationToken cancellationToken) { var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var checksum = await GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false); - return await LoadOrCreateSourceSymbolTreeInfoAsync( project.Solution, compilation.Assembly, checksum, project.FilePath, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); From 87a3d4f3f67d409550fd0677542439c949599fad Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:23:15 -0700 Subject: [PATCH 057/214] No need to store checksum when the SymbolTreeIndex already stores the checksum. --- ...mbolTreeInfoIncrementalAnalyzerProvider.cs | 38 ++++++------------- .../FindSymbols/SymbolTree/SymbolTreeInfo.cs | 6 +-- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 2 +- .../SymbolTreeInfo_Serialization.cs | 4 +- 4 files changed, 17 insertions(+), 33 deletions(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index 9bee5bf9d2814..be29068832116 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -38,22 +38,8 @@ namespace Microsoft.CodeAnalysis.IncrementalCaches [ExportWorkspaceServiceFactory(typeof(ISymbolTreeInfoCacheService))] internal class SymbolTreeInfoIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider, IWorkspaceServiceFactory { - private struct ProjectInfo - { - public readonly Checksum Checksum; - public readonly SymbolTreeInfo SymbolTreeInfo; - - public ProjectInfo(Checksum checksum, SymbolTreeInfo info) - { - Checksum = checksum; - SymbolTreeInfo = info; - } - } - private struct MetadataInfo { - public readonly Checksum Checksum; - /// /// Note: can be null if were unable to create a SymbolTreeInfo /// (for example, if the metadata was bogus and we couldn't read it in). @@ -67,9 +53,8 @@ private struct MetadataInfo /// public readonly HashSet ReferencingProjects; - public MetadataInfo(Checksum checksum, SymbolTreeInfo info, HashSet referencingProjects) + public MetadataInfo(SymbolTreeInfo info, HashSet referencingProjects) { - Checksum = checksum; SymbolTreeInfo = info; ReferencingProjects = referencingProjects; } @@ -77,7 +62,7 @@ public MetadataInfo(Checksum checksum, SymbolTreeInfo info, HashSet r // Concurrent dictionaries so they can be read from the SymbolTreeInfoCacheService while // they are being populated/updated by the IncrementalAnalyzer. - private readonly ConcurrentDictionary _projectToInfo = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _projectToInfo = new ConcurrentDictionary(); private readonly ConcurrentDictionary _metadataPathToInfo = new ConcurrentDictionary(); public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) @@ -109,11 +94,11 @@ private static string GetReferenceKey(PortableExecutableReference reference) private class SymbolTreeInfoCacheService : ISymbolTreeInfoCacheService { - private readonly ConcurrentDictionary _projectToInfo; + private readonly ConcurrentDictionary _projectToInfo; private readonly ConcurrentDictionary _metadataPathToInfo; public SymbolTreeInfoCacheService( - ConcurrentDictionary projectToInfo, + ConcurrentDictionary projectToInfo, ConcurrentDictionary metadataPathToInfo) { _projectToInfo = projectToInfo; @@ -131,7 +116,7 @@ public async Task TryGetMetadataSymbolTreeInfoAsync( if (key != null) { if (_metadataPathToInfo.TryGetValue(key, out var metadataInfo) && - metadataInfo.Checksum == checksum) + metadataInfo.SymbolTreeInfo.Checksum == checksum) { return metadataInfo.SymbolTreeInfo; } @@ -151,7 +136,7 @@ public async Task TryGetSourceSymbolTreeInfoAsync( if (_projectToInfo.TryGetValue(project.Id, out var projectInfo) && projectInfo.Checksum == await SymbolTreeInfo.GetSourceSymbolsChecksumAsync(project, cancellationToken).ConfigureAwait(false)) { - return projectInfo.SymbolTreeInfo; + return projectInfo; } return null; @@ -160,11 +145,11 @@ public async Task TryGetSourceSymbolTreeInfoAsync( private class IncrementalAnalyzer : IncrementalAnalyzerBase { - private readonly ConcurrentDictionary _projectToInfo; + private readonly ConcurrentDictionary _projectToInfo; private readonly ConcurrentDictionary _metadataPathToInfo; public IncrementalAnalyzer( - ConcurrentDictionary projectToInfo, + ConcurrentDictionary projectToInfo, ConcurrentDictionary metadataPathToInfo) { _projectToInfo = projectToInfo; @@ -223,12 +208,11 @@ private async Task UpdateSourceSymbolTreeInfoAsync(Project project, Cancellation if (!_projectToInfo.TryGetValue(project.Id, out var projectInfo) || projectInfo.Checksum != checksum) { - var info = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( + projectInfo = await SymbolTreeInfo.GetInfoForSourceAssemblyAsync( project, checksum, cancellationToken).ConfigureAwait(false); // Mark that we're up to date with this project. Future calls with the same // semantic version can bail out immediately. - projectInfo = new ProjectInfo(checksum, info); _projectToInfo.AddOrUpdate(project.Id, projectInfo, (_1, _2) => projectInfo); } } @@ -254,7 +238,7 @@ private async Task UpdateReferenceAsync( var checksum = SymbolTreeInfo.GetMetadataChecksum(project.Solution, reference, cancellationToken); if (!_metadataPathToInfo.TryGetValue(key, out var metadataInfo) || - metadataInfo.Checksum != checksum) + metadataInfo.SymbolTreeInfo.Checksum != checksum) { var info = await SymbolTreeInfo.TryGetInfoForMetadataReferenceAsync( project.Solution, reference, checksum, loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -262,7 +246,7 @@ private async Task UpdateReferenceAsync( // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. - metadataInfo = new MetadataInfo(checksum, info, metadataInfo.ReferencingProjects ?? new HashSet()); + metadataInfo = new MetadataInfo( info, metadataInfo.ReferencingProjects ?? new HashSet()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 09ff46e11e176..7284be192a654 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.FindSymbols { internal partial class SymbolTreeInfo { - private readonly Checksum _checksum; + public readonly Checksum Checksum; /// /// To prevent lots of allocations, we concatenate all the names in all our @@ -108,7 +108,7 @@ private SymbolTreeInfo( Node[] sortedNodes, Task spellCheckerTask) { - _checksum = checksum; + Checksum = checksum; _concatenatedNames = concatenatedNames; _nodes = ImmutableArray.Create(sortedNodes); _spellCheckerTask = spellCheckerTask; @@ -472,7 +472,7 @@ private string GetName(Node node) internal void AssertEquivalentTo(SymbolTreeInfo other) { - Debug.Assert(_checksum.Equals(other._checksum)); + Debug.Assert(Checksum.Equals(other.Checksum)); Debug.Assert(_concatenatedNames == other._concatenatedNames); Debug.Assert(_nodes.Length == other._nodes.Length); diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index f3c19ad5a6f1a..269128f523139 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -163,7 +163,7 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( loadOnly, create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken), keySuffix: "_Metadata", - getPersistedChecksum: info => info._checksum, + getPersistedChecksum: info => info.Checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); } diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index c5a6d2fed76bf..bcc01dfa6b1f7 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -38,7 +38,7 @@ private static Task LoadOrCreateSourceSymbolTreeInfoAsync( loadOnly, create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken), keySuffix: "_Source", - getPersistedChecksum: info => info._checksum, + getPersistedChecksum: info => info.Checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); } @@ -140,7 +140,7 @@ private static async Task LoadOrCreateAsync( public void WriteTo(ObjectWriter writer) { writer.WriteString(SerializationFormat); - _checksum.WriteTo(writer); + Checksum.WriteTo(writer); writer.WriteString(_concatenatedNames); From 2450b9210f177d87fe80b8dc21e8674866c61814 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:25:32 -0700 Subject: [PATCH 058/214] Whitespace. --- .../SymbolTreeInfoIncrementalAnalyzerProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs index be29068832116..9f730f3180c94 100644 --- a/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs +++ b/src/Features/Core/Portable/IncrementalCaches/SymbolTreeInfoIncrementalAnalyzerProvider.cs @@ -246,7 +246,7 @@ private async Task UpdateReferenceAsync( // Note, getting the info may fail (for example, bogus metadata). That's ok. // We still want to cache that result so that don't try to continuously produce // this info over and over again. - metadataInfo = new MetadataInfo( info, metadataInfo.ReferencingProjects ?? new HashSet()); + metadataInfo = new MetadataInfo(info, metadataInfo.ReferencingProjects ?? new HashSet()); _metadataPathToInfo.AddOrUpdate(key, metadataInfo, (_1, _2) => metadataInfo); } From b33bfb1787fc58f09f7a2353d2bc78dc5d7fe448 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:40:17 -0700 Subject: [PATCH 059/214] Don't make the checksum dependent on the document checksum directly. The document checksum contains values that change across sessions. --- .../SymbolTree/SymbolTreeInfo_Source.cs | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 1ec7fd1ac8961..c6675caf304b5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -1,6 +1,9 @@ -using System; +// 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.Collections; @@ -43,9 +46,24 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project // changed. The only thing that can make those source-symbols change in that manner are if // the text of any document changes, or if options for the project change. So we build our // checksum out of that data. - var stateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - var checksum = Checksum.Create(nameof(SymbolTreeInfo), - new Checksum[] { stateChecksums.Documents.Checksum, stateChecksums.CompilationOptions, stateChecksums.ParseOptions }); + var serializer = new Serializer(project.Solution.Workspace); + + var orderedDocumentIds = ChecksumCache.GetOrCreate(project.DocumentIds, _ => project.DocumentIds.OrderBy(id => id.Id).ToImmutableArray()); + var textChecksumsTasks = orderedDocumentIds.Select(async id => + { + var text = await project.GetDocument(id).GetTextAsync(cancellationToken).ConfigureAwait(false); + return ChecksumCache.GetOrCreate(text, _ => serializer.CreateChecksum(text, cancellationToken)); + }); + + var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); + var compilationOptionsChecksum = ChecksumCache.GetOrCreate(project.CompilationOptions, _ => serializer.CreateChecksum(project.CompilationOptions, cancellationToken)); + var parseOptionsChecksum = ChecksumCache.GetOrCreate(project.ParseOptions, _ => serializer.CreateChecksum(project.ParseOptions, cancellationToken)); + + var allChecksums = textChecksums.ToList(); + allChecksums.Add(compilationOptionsChecksum); + allChecksums.Add(parseOptionsChecksum); + + var checksum = Checksum.Create(nameof(SymbolTreeInfo), allChecksums); return checksum; } From 1bd958012ef5e545508ef055f816197271658f61 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 12:41:35 -0700 Subject: [PATCH 060/214] No need to cache checksum here --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index c6675caf304b5..b08cb87a59353 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -52,7 +52,7 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project var textChecksumsTasks = orderedDocumentIds.Select(async id => { var text = await project.GetDocument(id).GetTextAsync(cancellationToken).ConfigureAwait(false); - return ChecksumCache.GetOrCreate(text, _ => serializer.CreateChecksum(text, cancellationToken)); + return serializer.CreateChecksum(text, cancellationToken); }); var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); From 08680232c8498494dce68d0b409e16e5b78c1332 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 29 Apr 2017 15:38:13 -0700 Subject: [PATCH 061/214] No point ordering by guid when the guid is fresh each time in VS. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index b08cb87a59353..51d104b1e9ce4 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -48,10 +48,9 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project // checksum out of that data. var serializer = new Serializer(project.Solution.Workspace); - var orderedDocumentIds = ChecksumCache.GetOrCreate(project.DocumentIds, _ => project.DocumentIds.OrderBy(id => id.Id).ToImmutableArray()); - var textChecksumsTasks = orderedDocumentIds.Select(async id => + var textChecksumsTasks = project.Documents.Select(async d => { - var text = await project.GetDocument(id).GetTextAsync(cancellationToken).ConfigureAwait(false); + var text = await d.GetTextAsync(cancellationToken).ConfigureAwait(false); return serializer.CreateChecksum(text, cancellationToken); }); From 19229ff9ff7ff2426c03236a23b4ad9eab5137c0 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 29 Mar 2017 18:23:06 -0500 Subject: [PATCH 062/214] Fix support for local functions in "Make Method Asynchronous" Fixes #14133 --- .../MakeMethodAsynchronousTests.cs | 83 +++++++++++++++++++ ...rpMakeMethodAsynchronousCodeFixProvider.cs | 28 ++++++- ...ctMakeMethodAsynchronousCodeFixProvider.cs | 4 +- 3 files changed, 109 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs index bd45b420d589c..ba9cd0714b2a8 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs @@ -599,5 +599,88 @@ async ValueTask TestAsync() }"; await TestInRegularAndScriptAsync(initial, expected); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")] + public async Task AddAsyncInLocalFunction() + { + await TestInRegularAndScriptAsync( +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + void M2() + { + [|await M3Async();|] + } + } + + async Task M3Async() + { + return 1; + } +}", +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + async Task M2Async() + { + await M3Async(); + } + } + + async Task M3Async() + { + return 1; + } +}"); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")] + public async Task AddAsyncInLocalFunctionKeepVoidReturn() + { + await TestInRegularAndScriptAsync( +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + void M2() + { + [|await M3Async();|] + } + } + + async Task M3Async() + { + return 1; + } +}", +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + async void M2Async() + { + await M3Async(); + } + } + + async Task M3Async() + { + return 1; + } +}", +index: 1); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs index 7676ce0266c8f..468c62c16412d 100644 --- a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs @@ -35,7 +35,9 @@ protected override string GetMakeAsyncVoidFunctionResource() protected override bool IsMethodOrAnonymousFunction(SyntaxNode node) { - return node.IsKind(SyntaxKind.MethodDeclaration) || node.IsAnyLambdaOrAnonymousMethod(); + return node.IsKind(SyntaxKind.MethodDeclaration) + || node.IsAnyLambdaOrAnonymousMethod() + || node.IsKind(SyntaxKind.LocalFunctionStatement); } protected override SyntaxNode AddAsyncTokenAndFixReturnType( @@ -45,6 +47,7 @@ protected override SyntaxNode AddAsyncTokenAndFixReturnType( switch (node) { case MethodDeclarationSyntax method: return FixMethod(keepVoid, methodSymbolOpt, method, taskType, taskOfTType, valueTaskOfTType); + case LocalFunctionStatementSyntax localFunction: return FixLocalFunction(keepVoid, methodSymbolOpt, localFunction, taskType, taskOfTType, valueTaskOfTType); case AnonymousMethodExpressionSyntax method: return FixAnonymousMethod(method); case ParenthesizedLambdaExpressionSyntax lambda: return FixParenthesizedLambda(lambda); case SimpleLambdaExpressionSyntax lambda: return FixSimpleLambda(lambda); @@ -56,7 +59,25 @@ private SyntaxNode FixMethod( bool keepVoid, IMethodSymbol methodSymbol, MethodDeclarationSyntax method, INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType) { - var newReturnType = method.ReturnType; + var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, method.ReturnType, taskType, taskOfTType, valueTaskOfTType); + var newModifiers = method.Modifiers.Add(s_asyncToken); + return method.WithReturnType(newReturnType).WithModifiers(newModifiers); + } + + private SyntaxNode FixLocalFunction( + bool keepVoid, IMethodSymbol methodSymbol, LocalFunctionStatementSyntax localFunction, + INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType) + { + var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, localFunction.ReturnType, taskType, taskOfTType, valueTaskOfTType); + var newModifiers = localFunction.Modifiers.Add(s_asyncToken); + return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers); + } + + private static TypeSyntax FixMethodReturnType( + bool keepVoid, IMethodSymbol methodSymbol, TypeSyntax returnType, + INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType) + { + var newReturnType = returnType; if (methodSymbol.ReturnsVoid) { @@ -75,8 +96,7 @@ private SyntaxNode FixMethod( } } - var newModifiers = method.Modifiers.Add(s_asyncToken); - return method.WithReturnType(newReturnType).WithModifiers(newModifiers); + return newReturnType; } private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda) diff --git a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index fbe6a70c245fa..f1ba9eaedebf3 100644 --- a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -51,7 +51,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // If it's a void returning method, offer to keep the void return type, or convert to // a Task return type. - if (symbol?.MethodKind == MethodKind.Ordinary && + if ((symbol?.MethodKind == MethodKind.Ordinary || symbol?.MethodKind == MethodKind.LocalFunction) && symbol.ReturnsVoid) { context.RegisterCodeFix( @@ -100,7 +100,7 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary && + if ((methodSymbolOpt?.MethodKind == MethodKind.Ordinary || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction) && !methodSymbolOpt.Name.EndsWith(AsyncSuffix)) { return await RenameThenAddAsyncTokenAsync( From 377629a9ed68ea805c9a5a11b822ddd9d4b476bf Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 29 Mar 2017 19:11:54 -0500 Subject: [PATCH 063/214] Fix support for local functions in "Make Method Synchronous" Related to #14133 --- .../MakeMethodSynchronousTests.cs | 29 +++++++++++ ...arpMakeMethodSynchronousCodeFixProvider.cs | 50 +++++++++++++------ ...actMakeMethodSynchronousCodeFixProvider.cs | 2 +- 3 files changed, 66 insertions(+), 15 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs index 8942b2569cda3..15f8ba82209bf 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs @@ -548,5 +548,34 @@ async void BarAsync() } }", ignoreTrivia: false); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(14133, "https://github.com/dotnet/roslyn/issues/14133")] + public async Task RemoveAsyncInLocalFunction() + { + await TestInRegularAndScriptAsync( +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + async Task [|M2Async|]() + { + } + } +}", +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + void M2() + { + } + } +}"); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs index 96d8e9a609312..45ecc5f43db61 100644 --- a/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs @@ -19,7 +19,9 @@ internal class CSharpMakeMethodSynchronousCodeFixProvider : AbstractMakeMethodSy protected override bool IsMethodOrAnonymousFunction(SyntaxNode node) { - return node.IsKind(SyntaxKind.MethodDeclaration) || node.IsAnyLambdaOrAnonymousMethod(); + return node.IsKind(SyntaxKind.MethodDeclaration) + || node.IsAnyLambdaOrAnonymousMethod() + || node.IsKind(SyntaxKind.LocalFunctionStatement); } protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType) @@ -27,6 +29,7 @@ protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol met switch (node) { case MethodDeclarationSyntax method: return FixMethod(methodSymbolOpt, method, taskType, taskOfTType); + case LocalFunctionStatementSyntax localFunction: return FixLocalFunction(methodSymbolOpt, localFunction, taskType, taskOfTType); case AnonymousMethodExpressionSyntax method: return FixAnonymousMethod(method); case ParenthesizedLambdaExpressionSyntax lambda: return FixParenthesizedLambda(lambda); case SimpleLambdaExpressionSyntax lambda: return FixSimpleLambda(lambda); @@ -36,47 +39,66 @@ protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol met private SyntaxNode FixMethod(IMethodSymbol methodSymbol, MethodDeclarationSyntax method, ITypeSymbol taskType, ITypeSymbol taskOfTType) { - var newReturnType = method.ReturnType; + var newReturnType = FixMethodReturnType(methodSymbol, method.ReturnType, taskType, taskOfTType); + var newModifiers = FixMethodModifiers(method.Modifiers, ref newReturnType); + return method.WithReturnType(newReturnType).WithModifiers(newModifiers); + } + + private SyntaxNode FixLocalFunction(IMethodSymbol methodSymbol, LocalFunctionStatementSyntax localFunction, ITypeSymbol taskType, ITypeSymbol taskOfTType) + { + var newReturnType = FixMethodReturnType(methodSymbol, localFunction.ReturnType, taskType, taskOfTType); + var newModifiers = FixMethodModifiers(localFunction.Modifiers, ref newReturnType); + return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers); + } + + private static TypeSyntax FixMethodReturnType(IMethodSymbol methodSymbol, TypeSyntax returnType, ITypeSymbol taskType, ITypeSymbol taskOfTType) + { + var newReturnType = returnType; // If the return type is Task, then make the new return type "T". // If it is Task, then make the new return type "void". if (methodSymbol.ReturnType.OriginalDefinition.Equals(taskType)) { - newReturnType = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)).WithTriviaFrom(method.ReturnType); + newReturnType = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword)).WithTriviaFrom(returnType); } else if (methodSymbol.ReturnType.OriginalDefinition.Equals(taskOfTType)) { - newReturnType = methodSymbol.ReturnType.GetTypeArguments()[0].GenerateTypeSyntax().WithTriviaFrom(method.ReturnType); + newReturnType = methodSymbol.ReturnType.GetTypeArguments()[0].GenerateTypeSyntax().WithTriviaFrom(returnType); } - var asyncTokenIndex = method.Modifiers.IndexOf(SyntaxKind.AsyncKeyword); + return newReturnType; + } + + private static SyntaxTokenList FixMethodModifiers(SyntaxTokenList modifiers, ref TypeSyntax newReturnType) + { + var asyncTokenIndex = modifiers.IndexOf(SyntaxKind.AsyncKeyword); SyntaxTokenList newModifiers; if (asyncTokenIndex == 0) { - // Have to move the trivia on teh async token appropriately. - var asyncLeadingTrivia = method.Modifiers[0].LeadingTrivia; + // Have to move the trivia on the async token appropriately. + var asyncLeadingTrivia = modifiers[0].LeadingTrivia; - if (method.Modifiers.Count > 1) + if (modifiers.Count > 1) { // Move the trivia to the next modifier; - newModifiers = method.Modifiers.Replace( - method.Modifiers[1], - method.Modifiers[1].WithPrependedLeadingTrivia(asyncLeadingTrivia)); + newModifiers = modifiers.Replace( + modifiers[1], + modifiers[1].WithPrependedLeadingTrivia(asyncLeadingTrivia)); newModifiers = newModifiers.RemoveAt(0); } else { // move it to the return type. - newModifiers = method.Modifiers.RemoveAt(0); + newModifiers = modifiers.RemoveAt(0); newReturnType = newReturnType.WithPrependedLeadingTrivia(asyncLeadingTrivia); } } else { - newModifiers = method.Modifiers.RemoveAt(asyncTokenIndex); + newModifiers = modifiers.RemoveAt(asyncTokenIndex); } - return method.WithReturnType(newReturnType).WithModifiers(newModifiers); + return newModifiers; } private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda) diff --git a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index 838dd461c1cc8..10aa6259356bb 100644 --- a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -49,7 +49,7 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - if (methodSymbolOpt?.MethodKind == MethodKind.Ordinary && + if ((methodSymbolOpt?.MethodKind == MethodKind.Ordinary || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction) && methodSymbolOpt.Name.Length > AsyncSuffix.Length && methodSymbolOpt.Name.EndsWith(AsyncSuffix)) { From 02cdda159a631e0c8675e8a230cc43778f6a4c32 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 3 Apr 2017 12:54:33 -0500 Subject: [PATCH 064/214] Fix handling of trivia in Make Method Asynchronous --- .../MakeMethodAsynchronousTests.cs | 92 +++++++++++++++++++ ...rpMakeMethodAsynchronousCodeFixProvider.cs | 17 +++- ...ctMakeMethodAsynchronousCodeFixProvider.cs | 4 +- 3 files changed, 107 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs index ba9cd0714b2a8..e3e978a102ef5 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodAsynchronous/MakeMethodAsynchronousTests.cs @@ -682,5 +682,97 @@ async Task M3Async() }", index: 1); } + + [Theory] + [InlineData(0, "Task")] + [InlineData(1, "void", Skip = "https://github.com/dotnet/roslyn/issues/18396")] + [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")] + public async Task AddAsyncInLocalFunctionKeepsTrivia(int codeFixIndex, string expectedReturn) + { + await TestInRegularAndScriptAsync( +@"using System.Threading.Tasks; + +class C +{ + public void M1() + { + // Leading trivia + /*1*/ void /*2*/ M2/*3*/() /*4*/ + { + [|await M3Async();|] + } + } + + async Task M3Async() + { + return 1; + } +}", +$@"using System.Threading.Tasks; + +class C +{{ + public void M1() + {{ + // Leading trivia + /*1*/ async {expectedReturn} /*2*/ M2Async/*3*/() /*4*/ + {{ + await M3Async(); + }} + }} + + async Task M3Async() + {{ + return 1; + }} +}}", + index: codeFixIndex, + ignoreTrivia: false); + } + + [Theory] + [InlineData("", 0, "Task")] + [InlineData("", 1, "void", Skip = "https://github.com/dotnet/roslyn/issues/18396")] + [InlineData("public", 0, "Task")] + [InlineData("public", 1, "void")] + [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")] + public async Task AddAsyncKeepsTrivia(string modifiers, int codeFixIndex, string expectedReturn) + { + await TestInRegularAndScriptAsync( +$@"using System.Threading.Tasks; + +class C +{{ + // Leading trivia + {modifiers}/*1*/ void /*2*/ M2/*3*/() /*4*/ + {{ + [|await M3Async();|] + }} + + async Task M3Async() + {{ + return 1; + }} +}}", +$@"using System.Threading.Tasks; + +class C +{{ + // Leading trivia + {modifiers}/*1*/ async {expectedReturn} /*2*/ M2Async/*3*/() /*4*/ + {{ + await M3Async(); + }} + + async Task M3Async() + {{ + return 1; + }} +}}", + index: codeFixIndex, + ignoreTrivia: false); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs index 468c62c16412d..2eedeea3bf7ec 100644 --- a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs @@ -60,7 +60,7 @@ private SyntaxNode FixMethod( INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType) { var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, method.ReturnType, taskType, taskOfTType, valueTaskOfTType); - var newModifiers = method.Modifiers.Add(s_asyncToken); + var newModifiers = AddAsyncModifierWithCorrectedTrivia(method.Modifiers, ref newReturnType); return method.WithReturnType(newReturnType).WithModifiers(newModifiers); } @@ -69,7 +69,7 @@ private SyntaxNode FixLocalFunction( INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType) { var newReturnType = FixMethodReturnType(keepVoid, methodSymbol, localFunction.ReturnType, taskType, taskOfTType, valueTaskOfTType); - var newModifiers = localFunction.Modifiers.Add(s_asyncToken); + var newModifiers = AddAsyncModifierWithCorrectedTrivia(localFunction.Modifiers, ref newReturnType); return localFunction.WithReturnType(newReturnType).WithModifiers(newModifiers); } @@ -96,7 +96,18 @@ private static TypeSyntax FixMethodReturnType( } } - return newReturnType; + return newReturnType.WithTriviaFrom(returnType); + } + + private static SyntaxTokenList AddAsyncModifierWithCorrectedTrivia(SyntaxTokenList modifiers, ref TypeSyntax newReturnType) + { + if (modifiers.Any()) + return modifiers.Add(s_asyncToken); + + // Move the leading trivia from the return type to the new modifiers list. + SyntaxTokenList result = SyntaxFactory.TokenList(s_asyncToken.WithLeadingTrivia(newReturnType.GetLeadingTrivia())); + newReturnType = newReturnType.WithoutLeadingTrivia(); + return result; } private SyntaxNode FixParenthesizedLambda(ParenthesizedLambdaExpressionSyntax lambda) diff --git a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index f1ba9eaedebf3..7ac4173692081 100644 --- a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -6,7 +6,6 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.Formatting; using Microsoft.CodeAnalysis.Rename; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -152,8 +151,7 @@ private async Task AddAsyncTokenAsync( var compilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); var (taskType, taskOfTType, valueTaskOfTType) = GetTaskTypes(compilation); - var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType, valueTaskOfTType) - .WithAdditionalAnnotations(Formatter.Annotation); + var newNode = AddAsyncTokenAndFixReturnType(keepVoid, methodSymbolOpt, node, taskType, taskOfTType, valueTaskOfTType); var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); var newRoot = root.ReplaceNode(node, newNode); From fe7c98d791178cfe7c7a6a6cc4f214acdb6b7c9d Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 3 Apr 2017 13:04:45 -0500 Subject: [PATCH 065/214] Add tests for trivia in Make Method Synchronous --- .../MakeMethodSynchronousTests.cs | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs index 15f8ba82209bf..a60f6c8bee688 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/MakeMethodSynchronous/MakeMethodSynchronousTests.cs @@ -577,5 +577,83 @@ void M2() } }"); } + + [Theory] + [InlineData("Task", "C")] + [InlineData("Task", "int")] + [InlineData("Task", "void")] + [InlineData("void", "void")] + [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")] + public async Task RemoveAsyncInLocalFunctionKeepsTrivia(string asyncReturn, string expectedReturn) + { + await TestInRegularAndScriptAsync( +$@"using System; +using System.Threading.Tasks; + +class C +{{ + public void M1() + {{ + // Leading trivia + /*1*/ async {asyncReturn} /*2*/ [|M2Async|]/*3*/() /*4*/ + {{ + throw new NotImplementedException(); + }} + }} +}}", +$@"using System; +using System.Threading.Tasks; + +class C +{{ + public void M1() + {{ + // Leading trivia + /*1*/ {expectedReturn} /*2*/ M2/*3*/() /*4*/ + {{ + throw new NotImplementedException(); + }} + }} +}}"); + } + + [Theory] + [InlineData("", "Task", "C")] + [InlineData("", "Task", "int")] + [InlineData("", "Task", "void")] + [InlineData("", "void", "void")] + [InlineData("public", "Task", "C")] + [InlineData("public", "Task", "int")] + [InlineData("public", "Task", "void")] + [InlineData("public", "void", "void")] + [Trait(Traits.Feature, Traits.Features.CodeActionsMakeMethodAsynchronous)] + [WorkItem(18307, "https://github.com/dotnet/roslyn/issues/18307")] + public async Task RemoveAsyncKeepsTrivia(string modifiers, string asyncReturn, string expectedReturn) + { + await TestInRegularAndScriptAsync( +$@"using System; +using System.Threading.Tasks; + +class C +{{ + // Leading trivia + {modifiers}/*1*/ async {asyncReturn} /*2*/ [|M2Async|]/*3*/() /*4*/ + {{ + throw new NotImplementedException(); + }} +}}", +$@"using System; +using System.Threading.Tasks; + +class C +{{ + // Leading trivia + {modifiers}/*1*/ {expectedReturn} /*2*/ M2/*3*/() /*4*/ + {{ + throw new NotImplementedException(); + }} +}}"); + } } } \ No newline at end of file From 3cf00f24076db770747644e81da9b2f5de30c841 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 3 Apr 2017 13:29:57 -0500 Subject: [PATCH 066/214] Use local variables instead of mixing && with || --- .../AbstractMakeMethodAsynchronousCodeFixProvider.cs | 12 ++++++++---- .../AbstractMakeMethodSynchronousCodeFixProvider.cs | 5 ++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index 7ac4173692081..5a89a606819f3 100644 --- a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -50,8 +50,10 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // If it's a void returning method, offer to keep the void return type, or convert to // a Task return type. - if ((symbol?.MethodKind == MethodKind.Ordinary || symbol?.MethodKind == MethodKind.LocalFunction) && - symbol.ReturnsVoid) + bool isOrdinaryOrLocalFunction = + symbol?.MethodKind == MethodKind.Ordinary + || symbol?.MethodKind == MethodKind.LocalFunction; + if (isOrdinaryOrLocalFunction && symbol.ReturnsVoid) { context.RegisterCodeFix( new MyCodeAction(GetMakeAsyncTaskFunctionResource(), c => FixNodeAsync( @@ -99,8 +101,10 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - if ((methodSymbolOpt?.MethodKind == MethodKind.Ordinary || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction) && - !methodSymbolOpt.Name.EndsWith(AsyncSuffix)) + bool isOrdinaryOrLocalFunction = + methodSymbolOpt?.MethodKind == MethodKind.Ordinary + || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction; + if (isOrdinaryOrLocalFunction && !methodSymbolOpt.Name.EndsWith(AsyncSuffix)) { return await RenameThenAddAsyncTokenAsync( keepVoid, document, node, methodSymbolOpt, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index 10aa6259356bb..6d8c1395bb1db 100644 --- a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -49,7 +49,10 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - if ((methodSymbolOpt?.MethodKind == MethodKind.Ordinary || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction) && + bool isOrdinaryOrLocalFunction = + methodSymbolOpt?.MethodKind == MethodKind.Ordinary + || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction; + if (isOrdinaryOrLocalFunction && methodSymbolOpt.Name.Length > AsyncSuffix.Length && methodSymbolOpt.Name.EndsWith(AsyncSuffix)) { From 09f3547178070a55a98c8d2187799c5c799781b9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 1 May 2017 10:48:09 -0500 Subject: [PATCH 067/214] Extract helper method IsOrdinaryMethodOrLocalFunction --- .../AbstractMakeMethodAsynchronousCodeFixProvider.cs | 8 ++------ .../AbstractMakeMethodSynchronousCodeFixProvider.cs | 4 +--- .../Portable/Shared/Extensions/ISymbolExtensions.cs | 11 +++++++++++ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index 5a89a606819f3..0d3074ea21ac1 100644 --- a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -50,9 +50,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context) // If it's a void returning method, offer to keep the void return type, or convert to // a Task return type. - bool isOrdinaryOrLocalFunction = - symbol?.MethodKind == MethodKind.Ordinary - || symbol?.MethodKind == MethodKind.LocalFunction; + bool isOrdinaryOrLocalFunction = symbol.IsOrdinaryMethodOrLocalFunction(); if (isOrdinaryOrLocalFunction && symbol.ReturnsVoid) { context.RegisterCodeFix( @@ -101,9 +99,7 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - bool isOrdinaryOrLocalFunction = - methodSymbolOpt?.MethodKind == MethodKind.Ordinary - || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction; + bool isOrdinaryOrLocalFunction = methodSymbolOpt.IsOrdinaryMethodOrLocalFunction(); if (isOrdinaryOrLocalFunction && !methodSymbolOpt.Name.EndsWith(AsyncSuffix)) { return await RenameThenAddAsyncTokenAsync( diff --git a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index 6d8c1395bb1db..6e9269b69506c 100644 --- a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -49,9 +49,7 @@ private async Task FixNodeAsync( var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false); var methodSymbolOpt = semanticModel.GetDeclaredSymbol(node) as IMethodSymbol; - bool isOrdinaryOrLocalFunction = - methodSymbolOpt?.MethodKind == MethodKind.Ordinary - || methodSymbolOpt?.MethodKind == MethodKind.LocalFunction; + bool isOrdinaryOrLocalFunction = methodSymbolOpt.IsOrdinaryMethodOrLocalFunction(); if (isOrdinaryOrLocalFunction && methodSymbolOpt.Name.Length > AsyncSuffix.Length && methodSymbolOpt.Name.EndsWith(AsyncSuffix)) diff --git a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs index a546ba643499e..f40c71e5f3d64 100644 --- a/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs +++ b/src/Workspaces/Core/Portable/Shared/Extensions/ISymbolExtensions.cs @@ -253,6 +253,17 @@ public static bool IsOrdinaryMethod(this ISymbol symbol) return (symbol as IMethodSymbol)?.MethodKind == MethodKind.Ordinary; } + public static bool IsOrdinaryMethodOrLocalFunction(this ISymbol symbol) + { + if (!(symbol is IMethodSymbol method)) + { + return false; + } + + return method.MethodKind == MethodKind.Ordinary + || method.MethodKind == MethodKind.LocalFunction; + } + public static bool IsDelegateType(this ISymbol symbol) { return symbol is ITypeSymbol && ((ITypeSymbol)symbol).TypeKind == TypeKind.Delegate; From 1bb2cbd2fb38efbfd6df5764b8d417f6e4836f12 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Mon, 1 May 2017 10:54:45 -0500 Subject: [PATCH 068/214] Extract helper method IsAsyncSupportingFunctionSyntax --- ...CSharpMakeMethodAsynchronousCodeFixProvider.cs | 8 ++------ .../CSharpMakeMethodSynchronousCodeFixProvider.cs | 8 ++------ ...stractMakeMethodAsynchronousCodeFixProvider.cs | 4 ++-- ...bstractMakeMethodSynchronousCodeFixProvider.cs | 4 ++-- ...lBasicMakeMethodAsynchronousCodeFixProvider.vb | 9 ++------- ...alBasicMakeMethodSynchronousCodeFixProvider.vb | 9 ++------- .../Portable/Extensions/SyntaxNodeExtensions.cs | 7 +++++++ .../Portable/Extensions/SyntaxNodeExtensions.vb | 15 +++++++++++++++ 8 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs index 2eedeea3bf7ec..a8c795ba0dc4e 100644 --- a/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMethodAsynchronous/CSharpMakeMethodAsynchronousCodeFixProvider.cs @@ -33,12 +33,8 @@ protected override string GetMakeAsyncVoidFunctionResource() return CSharpFeaturesResources.Make_method_async_remain_void; } - protected override bool IsMethodOrAnonymousFunction(SyntaxNode node) - { - return node.IsKind(SyntaxKind.MethodDeclaration) - || node.IsAnyLambdaOrAnonymousMethod() - || node.IsKind(SyntaxKind.LocalFunctionStatement); - } + protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node) + => node.IsAsyncSupportingFunctionSyntax(); protected override SyntaxNode AddAsyncTokenAndFixReturnType( bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node, diff --git a/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs b/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs index 45ecc5f43db61..afed98fce2574 100644 --- a/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/MakeMethodSynchronous/CSharpMakeMethodSynchronousCodeFixProvider.cs @@ -17,12 +17,8 @@ internal class CSharpMakeMethodSynchronousCodeFixProvider : AbstractMakeMethodSy public override ImmutableArray FixableDiagnosticIds { get; } = ImmutableArray.Create(CS1998); - protected override bool IsMethodOrAnonymousFunction(SyntaxNode node) - { - return node.IsKind(SyntaxKind.MethodDeclaration) - || node.IsAnyLambdaOrAnonymousMethod() - || node.IsKind(SyntaxKind.LocalFunctionStatement); - } + protected override bool IsAsyncSupportingFunctionSyntax(SyntaxNode node) + => node.IsAsyncSupportingFunctionSyntax(); protected override SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType) { diff --git a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs index 0d3074ea21ac1..c4103f2154d66 100644 --- a/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodAsynchronous/AbstractMakeMethodAsynchronousCodeFixProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.MakeMethodAsynchronous { internal abstract class AbstractMakeMethodAsynchronousCodeFixProvider : CodeFixProvider { - protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node); + protected abstract bool IsAsyncSupportingFunctionSyntax(SyntaxNode node); protected abstract SyntaxNode AddAsyncTokenAndFixReturnType( bool keepVoid, IMethodSymbol methodSymbolOpt, SyntaxNode node, INamedTypeSymbol taskType, INamedTypeSymbol taskOfTType, INamedTypeSymbol valueTaskOfTType); @@ -115,7 +115,7 @@ private async Task FixNodeAsync( private SyntaxNode GetContainingFunction(Diagnostic diagnostic, CancellationToken cancellationToken) { var token = diagnostic.Location.FindToken(cancellationToken); - var node = token.GetAncestor(IsMethodOrAnonymousFunction); + var node = token.GetAncestor(IsAsyncSupportingFunctionSyntax); return node; } diff --git a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs index 6e9269b69506c..864434ac384af 100644 --- a/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs +++ b/src/Features/Core/Portable/MakeMethodSynchronous/AbstractMakeMethodSynchronousCodeFixProvider.cs @@ -22,7 +22,7 @@ internal abstract class AbstractMakeMethodSynchronousCodeFixProvider : CodeFixPr { public static readonly string EquivalenceKey = FeaturesResources.Make_method_synchronous; - protected abstract bool IsMethodOrAnonymousFunction(SyntaxNode node); + protected abstract bool IsAsyncSupportingFunctionSyntax(SyntaxNode node); protected abstract SyntaxNode RemoveAsyncTokenAndFixReturnType(IMethodSymbol methodSymbolOpt, SyntaxNode node, ITypeSymbol taskType, ITypeSymbol taskOfTType); public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; @@ -41,7 +41,7 @@ private async Task FixNodeAsync( Document document, Diagnostic diagnostic, CancellationToken cancellationToken) { var token = diagnostic.Location.FindToken(cancellationToken); - var node = token.GetAncestor(IsMethodOrAnonymousFunction); + var node = token.GetAncestor(IsAsyncSupportingFunctionSyntax); // See if we're on an actual method declaration (otherwise we're on a lambda declaration). // If we're on a method declaration, we'll get an IMethodSymbol back. In that case, check diff --git a/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb b/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb index cdb6ebfa661c4..09a30912a7f1b 100644 --- a/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/MakeMethodAsynchronous/VisualBasicMakeMethodAsynchronousCodeFixProvider.vb @@ -35,13 +35,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodAsynchronous Return VBFeaturesResources.Make_Async_Sub End Function - Protected Overrides Function IsMethodOrAnonymousFunction(node As SyntaxNode) As Boolean - Return node.IsKind(SyntaxKind.FunctionBlock) OrElse - node.IsKind(SyntaxKind.SubBlock) OrElse - node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse - node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse - node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse - node.IsKind(SyntaxKind.SingleLineSubLambdaExpression) + Protected Overrides Function IsAsyncSupportingFunctionSyntax(node As SyntaxNode) As Boolean + Return node.IsAsyncSupportedFunctionSyntax() End Function Protected Overrides Function AddAsyncTokenAndFixReturnType( diff --git a/src/Features/VisualBasic/Portable/MakeMethodSynchronous/VisualBasicMakeMethodSynchronousCodeFixProvider.vb b/src/Features/VisualBasic/Portable/MakeMethodSynchronous/VisualBasicMakeMethodSynchronousCodeFixProvider.vb index 3a0e037f50700..e3ad9a10b79a3 100644 --- a/src/Features/VisualBasic/Portable/MakeMethodSynchronous/VisualBasicMakeMethodSynchronousCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/MakeMethodSynchronous/VisualBasicMakeMethodSynchronousCodeFixProvider.vb @@ -21,13 +21,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.MakeMethodSynchronous End Get End Property - Protected Overrides Function IsMethodOrAnonymousFunction(node As SyntaxNode) As Boolean - Return node.IsKind(SyntaxKind.FunctionBlock) OrElse - node.IsKind(SyntaxKind.SubBlock) OrElse - node.IsKind(SyntaxKind.MultiLineFunctionLambdaExpression) OrElse - node.IsKind(SyntaxKind.MultiLineSubLambdaExpression) OrElse - node.IsKind(SyntaxKind.SingleLineFunctionLambdaExpression) OrElse - node.IsKind(SyntaxKind.SingleLineSubLambdaExpression) + Protected Overrides Function IsAsyncSupportingFunctionSyntax(node As SyntaxNode) As Boolean + Return node.IsAsyncSupportedFunctionSyntax() End Function Protected Overrides Function RemoveAsyncTokenAndFixReturnType(methodSymbolOpt As IMethodSymbol, node As SyntaxNode, taskType As ITypeSymbol, taskOfTType As ITypeSymbol) As SyntaxNode diff --git a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs index 9702bdc3eed2a..985d5144e2eba 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/SyntaxNodeExtensions.cs @@ -295,6 +295,13 @@ public static TNode ConvertToSingleLine(this TNode node, bool useElasticT return (TNode)rewriter.Visit(node); } + public static bool IsAsyncSupportingFunctionSyntax(this SyntaxNode node) + { + return node.IsKind(SyntaxKind.MethodDeclaration) + || node.IsAnyLambdaOrAnonymousMethod() + || node.IsKind(SyntaxKind.LocalFunctionStatement); + } + public static bool IsAnyArgumentList(this SyntaxNode node) { return node.IsKind(SyntaxKind.ArgumentList) || diff --git a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb index 7ee14d53fbe15..1578b13e26a32 100644 --- a/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb +++ b/src/Workspaces/VisualBasic/Portable/Extensions/SyntaxNodeExtensions.vb @@ -188,6 +188,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Return Contract.FailWithReturn(Of SyntaxList(Of StatementSyntax))("unknown statements container!") End Function + + Friend Function IsAsyncSupportedFunctionSyntax(node As SyntaxNode) As Boolean + Select Case node?.Kind() + Case _ + SyntaxKind.FunctionBlock, + SyntaxKind.SubBlock, + SyntaxKind.MultiLineFunctionLambdaExpression, + SyntaxKind.MultiLineSubLambdaExpression, + SyntaxKind.SingleLineFunctionLambdaExpression, + SyntaxKind.SingleLineSubLambdaExpression + Return True + End Select + Return False + End Function + Friend Function IsMultiLineLambda(node As SyntaxNode) As Boolean Return SyntaxFacts.IsMultiLineLambdaExpression(node.Kind()) From 7c3e09879cf346d0e477fa9db61a57dac432fc32 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 1 May 2017 15:47:22 -0700 Subject: [PATCH 069/214] Address PR feedback --- .../CSharp/Portable/BoundTree/Expression.cs | 17 +- .../CSharp/Portable/BoundTree/Statement.cs | 4 +- .../AsyncRewriter/AwaitExpressionSpiller.cs | 2 - ...tionTests_IParameterReferenceExpression.cs | 43 +++ .../Operations/IOperationWithChildren.cs | 4 + .../Portable/BoundTree/Expression.vb | 266 ++++++++---------- .../Portable/BoundTree/Statement.vb | 21 +- 7 files changed, 181 insertions(+), 176 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index e22bc641c8d93..804f839396268 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -522,11 +522,11 @@ ImmutableArray IObjectCreationExpression.MemberInitializers return (ImmutableArray)s_memberInitializersMappings.GetValue(this, objectCreationExpression => { - var initializers = GetChildInitializers(objectCreationExpression.InitializerExpressionOpt); - if (!initializers.IsEmpty) + var objectInitializerExpression = this.InitializerExpressionOpt as BoundObjectInitializerExpression; + if (objectInitializerExpression != null) { - var builder = ArrayBuilder.GetInstance(initializers.Length); - foreach (var memberAssignment in initializers) + var builder = ArrayBuilder.GetInstance(objectInitializerExpression.Initializers.Length); + foreach (var memberAssignment in objectInitializerExpression.Initializers) { var assignment = memberAssignment as BoundAssignmentOperator; var leftSymbol = (assignment?.Left as BoundObjectInitializerMember)?.MemberSymbol; @@ -548,7 +548,6 @@ ImmutableArray IObjectCreationExpression.MemberInitializers } return builder.ToImmutableAndFree(); } - return ImmutableArray.Empty; }); } @@ -1503,8 +1502,6 @@ internal partial class BoundRangeVariable { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => ImmutableArray.Create(this.Value); - public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -1570,7 +1567,7 @@ internal partial class BoundPattern internal partial class BoundDeclarationPattern { - protected override ImmutableArray Children => ImmutableArray.Create(this.VariableAccess, this.DeclaredType); + protected override ImmutableArray Children => ImmutableArray.Empty; } internal partial class BoundConstantPattern @@ -1873,7 +1870,7 @@ internal partial class BoundDynamicInvocation { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.As().Add(this.Expression); + protected override ImmutableArray Children => this.Arguments.As().Insert(0, this.Expression); public override void Accept(OperationVisitor visitor) { @@ -1890,8 +1887,6 @@ internal partial class BoundArrayLength { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); - public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); diff --git a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs index 2fe6374dd1d8e..7a3bc47f1f1ba 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs @@ -32,6 +32,7 @@ internal partial class BoundNode : IOperation, IOperationWithChildren /// /// Override this property to return the child operations if the IOperation API corresponding to this bound node is not yet designed or implemented. /// + /// Note that any of the child operation nodes may be null. protected virtual ImmutableArray Children => ImmutableArray.Empty; public virtual void Accept(OperationVisitor visitor) @@ -795,8 +796,6 @@ internal partial class BoundConditionalGoto { protected override OperationKind StatementKind => OperationKind.None; - protected override ImmutableArray Children => ImmutableArray.Create(this.Condition); - public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -883,7 +882,6 @@ protected override ImmutableArray Children { builder.Add(section); } - builder.Add(this.DefaultLabel); return builder.ToImmutable(); } diff --git a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs index 692d6ac69428b..1306f545dbe01 100644 --- a/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/AsyncRewriter/AwaitExpressionSpiller.cs @@ -58,8 +58,6 @@ public bool HasLocals protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => ImmutableArray.Create(this.Value); - public override void Accept(OperationVisitor visitor) { throw ExceptionUtilities.Unreachable; diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index fc9f7c2d3a95a..69643cbbb9ace 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -576,5 +576,48 @@ public void M(int x) VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_DefaultPatternSwitchStatement() + { + string source = @" +internal class Class +{ + public void M(int x) + { + switch (x) + { + case var y when (x >= 10): + break; + + /**/default:/**/ + break; + } + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'switch (x) ... }') + Children(4): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'case var y ... break;') + Children(2): IOperation: (OperationKind.None) (Syntax: 'case var y ... (x >= 10):') + Children(2): IOperation: (OperationKind.None) (Syntax: 'var y') + Children(2): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') + IOperation: (OperationKind.None) (Syntax: 'var') + IBinaryOperatorExpression (BinaryOperationKind.IntegerGreaterThanOrEqual) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'x >= 10') + Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + Right: ILiteralExpression (Text: 10) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 10) (Syntax: '10') + IBranchStatement (BranchKind.Break) (OperationKind.BranchStatement) (Syntax: 'break;') + IOperation: (OperationKind.None) (Syntax: 'default:/*< ... break;') + Children(2): IOperation: (OperationKind.None) (Syntax: 'default:') + Children(1): IOperation: (OperationKind.None) (Syntax: 'default:') + IBranchStatement (BranchKind.Break) (OperationKind.BranchStatement) (Syntax: 'break;') + IOperation: (OperationKind.None) (Syntax: 'default:') + Children(1): IOperation: (OperationKind.None) (Syntax: 'default:') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } } } \ No newline at end of file diff --git a/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs b/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs index d949ba4a665da..e9dec76f560c7 100644 --- a/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs +++ b/src/Compilers/Core/Portable/Operations/IOperationWithChildren.cs @@ -14,6 +14,10 @@ namespace Microsoft.CodeAnalysis [InternalImplementationOnly] internal interface IOperationWithChildren: IOperation { + /// + /// An array of child operations for this operation. + /// + /// Note that any of the child operation nodes may be null. ImmutableArray Children { get; } } } diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index 7d29acc0ff28f..e5eea43a924a7 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -6,7 +6,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Symbols Namespace Microsoft.CodeAnalysis.VisualBasic - Partial Friend Class BoundExpression + Friend Partial Class BoundExpression Protected Overrides ReadOnly Property OperationKind As OperationKind Get Return Me.ExpressionKind @@ -49,7 +49,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAssignmentOperator + Friend Partial Class BoundAssignmentOperator Implements IAssignmentExpression Implements ICompoundAssignmentExpression @@ -138,7 +138,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMeReference + Friend Partial Class BoundMeReference Implements IInstanceReferenceExpression Private ReadOnly Property IInstanceReferenceExpression_InstanceReferenceKind As InstanceReferenceKind Implements IInstanceReferenceExpression.InstanceReferenceKind @@ -160,7 +160,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMyBaseReference + Friend Partial Class BoundMyBaseReference Implements IInstanceReferenceExpression Private ReadOnly Property IInstanceReferenceExpression_InstanceReferenceKind As InstanceReferenceKind Implements IInstanceReferenceExpression.InstanceReferenceKind @@ -182,7 +182,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMyClassReference + Friend Partial Class BoundMyClassReference Implements IInstanceReferenceExpression Private ReadOnly Property IInstanceReferenceExpression_InstanceReferenceKind As InstanceReferenceKind Implements IInstanceReferenceExpression.InstanceReferenceKind @@ -204,7 +204,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLiteral + Friend Partial Class BoundLiteral Implements ILiteralExpression Private ReadOnly Property ILiteralExpression_Text As String Implements ILiteralExpression.Text @@ -226,7 +226,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAwaitOperator + Friend Partial Class BoundAwaitOperator Implements IAwaitExpression Private ReadOnly Property IAwaitExpression_AwaitedValue As IOperation Implements IAwaitExpression.AwaitedValue @@ -248,7 +248,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLambda + Friend Partial Class BoundLambda Implements ILambdaExpression Private ReadOnly Property ILambdaExpression_Body As IBlockStatement Implements ILambdaExpression.Body @@ -276,7 +276,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundCall + Friend Partial Class BoundCall Implements IInvocationExpression Private ReadOnly Property IHasArgumentsExpression_ArgumentsInEvaluationOrder As ImmutableArray(Of IArgument) Implements IHasArgumentsExpression.ArgumentsInEvaluationOrder @@ -503,7 +503,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Class End Class - Partial Friend Class BoundOmittedArgument + Friend Partial Class BoundOmittedArgument Implements IOmittedArgumentExpression Protected Overrides Function ExpressionKind() As OperationKind @@ -519,7 +519,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundParenthesized + Friend Partial Class BoundParenthesized Implements IParenthesizedExpression Private ReadOnly Property IParenthesizedExpression_Operand As IOperation Implements IParenthesizedExpression.Operand @@ -541,7 +541,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundArrayAccess + Friend Partial Class BoundArrayAccess Implements IArrayElementReferenceExpression Private ReadOnly Property IArrayElementReferenceExpression_ArrayReference As IOperation Implements IArrayElementReferenceExpression.ArrayReference @@ -569,7 +569,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUnaryOperator + Friend Partial Class BoundUnaryOperator Implements IUnaryOperatorExpression Private ReadOnly Property IHasOperatorMethodExpression_OperatorMethod As IMethodSymbol Implements IHasOperatorMethodExpression.OperatorMethod @@ -609,7 +609,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUserDefinedUnaryOperator + Friend Partial Class BoundUserDefinedUnaryOperator Implements IUnaryOperatorExpression Private ReadOnly Property IHasOperatorMethodExpression_OperatorMethod As IMethodSymbol Implements IHasOperatorMethodExpression.OperatorMethod @@ -666,7 +666,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundBinaryOperator + Friend Partial Class BoundBinaryOperator Implements IBinaryOperatorExpression Private ReadOnly Property IBinaryOperatorExpression_LeftOperand As IOperation Implements IBinaryOperatorExpression.LeftOperand @@ -712,7 +712,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUserDefinedBinaryOperator + Friend Partial Class BoundUserDefinedBinaryOperator Implements IBinaryOperatorExpression Private ReadOnly Property IBinaryOperatorExpression_LeftOperand As IOperation Implements IBinaryOperatorExpression.LeftOperand @@ -810,7 +810,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundBinaryConditionalExpression + Friend Partial Class BoundBinaryConditionalExpression Implements INullCoalescingExpression Private ReadOnly Property INullCoalescingExpression_PrimaryOperand As IOperation Implements INullCoalescingExpression.PrimaryOperand @@ -838,7 +838,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUserDefinedShortCircuitingOperator + Friend Partial Class BoundUserDefinedShortCircuitingOperator Implements IBinaryOperatorExpression Private ReadOnly Property IBinaryOperatorExpression_LeftOperand As IOperation Implements IBinaryOperatorExpression.LeftOperand @@ -884,7 +884,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundBadExpression + Friend Partial Class BoundBadExpression Implements IInvalidExpression Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Implements IInvalidExpression.Children @@ -906,7 +906,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTryCast + Friend Partial Class BoundTryCast Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -952,7 +952,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundDirectCast + Friend Partial Class BoundDirectCast Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -998,7 +998,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundConversion + Friend Partial Class BoundConversion Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -1044,7 +1044,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUserDefinedConversion + Friend Partial Class BoundUserDefinedConversion Implements IConversionExpression Private ReadOnly Property IConversionExpression_ConversionKind As Semantics.ConversionKind Implements IConversionExpression.ConversionKind @@ -1090,7 +1090,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTernaryConditionalExpression + Friend Partial Class BoundTernaryConditionalExpression Implements IConditionalChoiceExpression Private ReadOnly Property IConditionalChoiceExpression_Condition As IOperation Implements IConditionalChoiceExpression.Condition @@ -1124,7 +1124,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTypeOf + Friend Partial Class BoundTypeOf Implements IIsTypeExpression Private ReadOnly Property IIsTypeExpression_IsType As ITypeSymbol Implements IIsTypeExpression.IsType @@ -1152,7 +1152,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundObjectCreationExpression + Friend Partial Class BoundObjectCreationExpression Implements IObjectCreationExpression Private Shared ReadOnly s_memberInitializersMappings As New System.Runtime.CompilerServices.ConditionalWeakTable(Of BoundObjectCreationExpression, Object) @@ -1340,7 +1340,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Class - Partial Friend Class BoundNewT + Friend Partial Class BoundNewT Implements ITypeParameterObjectCreationExpression Protected Overrides Function ExpressionKind() As OperationKind @@ -1356,7 +1356,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundArrayCreation + Friend Partial Class BoundArrayCreation Implements IArrayCreationExpression Private ReadOnly Property IArrayCreationExpression_DimensionSizes As ImmutableArray(Of IOperation) Implements IArrayCreationExpression.DimensionSizes @@ -1396,7 +1396,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundArrayInitialization + Friend Partial Class BoundArrayInitialization Implements IArrayInitializer Private ReadOnly Property IArrayInitializer_ElementValues As ImmutableArray(Of IOperation) Implements IArrayInitializer.ElementValues @@ -1417,7 +1417,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundPropertyAccess + Friend Partial Class BoundPropertyAccess Implements IIndexedPropertyReferenceExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1461,7 +1461,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundEventAccess + Friend Partial Class BoundEventAccess Implements IEventReferenceExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1499,7 +1499,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundDelegateCreationExpression + Friend Partial Class BoundDelegateCreationExpression Implements IMethodBindingExpression Private ReadOnly Property IMemberReferenceExpression_Instance As IOperation Implements IMemberReferenceExpression.Instance @@ -1543,7 +1543,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundFieldAccess + Friend Partial Class BoundFieldAccess Implements IFieldReferenceExpression Private ReadOnly Property IFieldReferenceExpression_Field As IFieldSymbol Implements IFieldReferenceExpression.Field @@ -1581,7 +1581,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundConditionalAccess + Friend Partial Class BoundConditionalAccess Implements IConditionalAccessExpression Private ReadOnly Property IConditionalAccessExpression_ConditionalValue As IOperation Implements IConditionalAccessExpression.ConditionalValue @@ -1609,7 +1609,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundConditionalAccessReceiverPlaceholder + Friend Partial Class BoundConditionalAccessReceiverPlaceholder Implements IConditionalAccessInstanceExpression Protected Overrides Function ExpressionKind() As OperationKind @@ -1625,7 +1625,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundParameter + Friend Partial Class BoundParameter Implements IParameterReferenceExpression Private ReadOnly Property IParameterReferenceExpression_Parameter As IParameterSymbol Implements IParameterReferenceExpression.Parameter @@ -1647,7 +1647,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLocal + Friend Partial Class BoundLocal Implements ILocalReferenceExpression Private ReadOnly Property ILocalReferenceExpression_Local As ILocalSymbol Implements ILocalReferenceExpression.Local @@ -1669,7 +1669,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLateMemberAccess + Friend Partial Class BoundLateMemberAccess Implements ILateBoundMemberReferenceExpression Private ReadOnly Property ILateBoundMemberReferenceExpression_Instance As IOperation Implements ILateBoundMemberReferenceExpression.Instance @@ -1697,7 +1697,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundFieldInitializer + Friend Partial Class BoundFieldInitializer Implements IFieldInitializer Private ReadOnly Property IFieldInitializer_InitializedFields As ImmutableArray(Of IFieldSymbol) Implements IFieldInitializer.InitializedFields @@ -1725,7 +1725,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundPropertyInitializer + Friend Partial Class BoundPropertyInitializer Implements IPropertyInitializer Private ReadOnly Property IPropertyInitializer_InitializedProperty As IPropertySymbol Implements IPropertyInitializer.InitializedProperty @@ -1753,7 +1753,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundParameterEqualsValue + Friend Partial Class BoundParameterEqualsValue Implements IParameterInitializer Private ReadOnly Property IOperation_IsInvalid As Boolean Implements IOperation.IsInvalid @@ -1807,7 +1807,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTypeArguments + Friend Partial Class BoundTypeArguments Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1841,7 +1841,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundWithLValueExpressionPlaceholder + Friend Partial Class BoundWithLValueExpressionPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1855,7 +1855,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundWithRValueExpressionPlaceholder + Friend Partial Class BoundWithRValueExpressionPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1869,7 +1869,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundRValuePlaceholder + Friend Partial Class BoundRValuePlaceholder Implements IPlaceholderExpression Protected Overrides Function ExpressionKind() As OperationKind @@ -1885,7 +1885,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLValuePlaceholder + Friend Partial Class BoundLValuePlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1899,7 +1899,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundDup + Friend Partial Class BoundDup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1913,7 +1913,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundBadVariable + Friend Partial Class BoundBadVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1927,17 +1927,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundArrayLength + Friend Partial Class BoundArrayLength Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.Expression) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1947,7 +1941,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundGetType + Friend Partial Class BoundGetType Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1967,7 +1961,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundFieldInfo + Friend Partial Class BoundFieldInfo Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1981,7 +1975,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMethodInfo + Friend Partial Class BoundMethodInfo Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -1995,7 +1989,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTypeExpression + Friend Partial Class BoundTypeExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2009,7 +2003,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTypeOrValueExpression + Friend Partial Class BoundTypeOrValueExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2023,7 +2017,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundNamespaceExpression + Friend Partial Class BoundNamespaceExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2037,7 +2031,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundNullableIsTrueOperator + Friend Partial Class BoundNullableIsTrueOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2057,7 +2051,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundCompoundAssignmentTargetPlaceholder + Friend Partial Class BoundCompoundAssignmentTargetPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2071,17 +2065,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundReferenceAssignment + Friend Partial Class BoundReferenceAssignment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.LValue) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2091,7 +2079,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAddressOfOperator + Friend Partial Class BoundAddressOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2105,7 +2093,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundSequencePointExpression + Friend Partial Class BoundSequencePointExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2119,7 +2107,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMethodGroup + Friend Partial Class BoundMethodGroup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2133,7 +2121,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundPropertyGroup + Friend Partial Class BoundPropertyGroup Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2147,7 +2135,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAttribute + Friend Partial Class BoundAttribute Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2167,7 +2155,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend MustInherit Class BoundTupleExpression + Friend Partial MustInherit Class BoundTupleExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2187,7 +2175,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLateInvocation + Friend Partial Class BoundLateInvocation Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2207,7 +2195,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLateAddressOfOperator + Friend Partial Class BoundLateAddressOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2227,7 +2215,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundNoPiaObjectCreationExpression + Friend Partial Class BoundNoPiaObjectCreationExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2247,7 +2235,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAnonymousTypeCreationExpression + Friend Partial Class BoundAnonymousTypeCreationExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2267,7 +2255,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAnonymousTypePropertyAccess + Friend Partial Class BoundAnonymousTypePropertyAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2281,7 +2269,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAnonymousTypeFieldInitializer + Friend Partial Class BoundAnonymousTypeFieldInitializer Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2301,7 +2289,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundObjectInitializerExpression + Friend Partial Class BoundObjectInitializerExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2321,7 +2309,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundCollectionInitializerExpression + Friend Partial Class BoundCollectionInitializerExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2341,7 +2329,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundArrayLiteral + Friend Partial Class BoundArrayLiteral Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2361,7 +2349,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundSequence + Friend Partial Class BoundSequence Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2375,7 +2363,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundValueTypeMeReference + Friend Partial Class BoundValueTypeMeReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2389,7 +2377,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundPreviousSubmissionReference + Friend Partial Class BoundPreviousSubmissionReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2403,7 +2391,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundHostObjectMemberReference + Friend Partial Class BoundHostObjectMemberReference Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2417,7 +2405,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundPseudoVariable + Friend Partial Class BoundPseudoVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2431,7 +2419,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundByRefArgumentPlaceholder + Friend Partial Class BoundByRefArgumentPlaceholder Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2445,14 +2433,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundByRefArgumentWithCopyBack + Friend Partial Class BoundByRefArgumentWithCopyBack Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Get - Return ImmutableArray.Create(Of IOperation)(Me.OriginalArgument, Me.InConversion, Me.InPlaceholder, Me.OutConversion, Me.OutPlaceholder) + Return ImmutableArray.Create(Of IOperation)(Me.OriginalArgument) End Get End Property @@ -2465,7 +2453,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLateBoundArgumentSupportingAssignmentWithCapture + Friend Partial Class BoundLateBoundArgumentSupportingAssignmentWithCapture Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2485,7 +2473,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLabel + Friend Partial Class BoundLabel Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2499,7 +2487,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class UnboundLambda + Friend Partial Class UnboundLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2513,7 +2501,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundQueryExpression + Friend Partial Class BoundQueryExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2533,11 +2521,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundQueryPart + Friend Partial Class BoundQueryPart Protected MustOverride Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) End Class - Partial Friend Class BoundQuerySource + Friend Partial Class BoundQuerySource Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2557,7 +2545,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundToQueryableCollectionConversion + Friend Partial Class BoundToQueryableCollectionConversion Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2577,7 +2565,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundQueryableSource + Friend Partial Class BoundQueryableSource Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2597,7 +2585,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundQueryClause + Friend Partial Class BoundQueryClause Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2617,7 +2605,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundOrdering + Friend Partial Class BoundOrdering Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2637,7 +2625,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundQueryLambda + Friend Partial Class BoundQueryLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2657,7 +2645,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundRangeVariableAssignment + Friend Partial Class BoundRangeVariableAssignment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2677,7 +2665,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class GroupTypeInferenceLambda + Friend Partial Class GroupTypeInferenceLambda Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2691,14 +2679,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundAggregateClause + Friend Partial Class BoundAggregateClause Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Get - Return ImmutableArray.Create(Of IOperation)(Me.CapturedGroupOpt, Me.GroupPlaceholderOpt, Me.UnderlyingExpression) + Return ImmutableArray.Create(Of IOperation)(Me.CapturedGroupOpt, Me.UnderlyingExpression) End Get End Property @@ -2711,7 +2699,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundGroupAggregation + Friend Partial Class BoundGroupAggregation Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2731,7 +2719,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundRangeVariable + Friend Partial Class BoundRangeVariable Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2745,7 +2733,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlName + Friend Partial Class BoundXmlName Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2759,7 +2747,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlNamespace + Friend Partial Class BoundXmlNamespace Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2773,7 +2761,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlDocument + Friend Partial Class BoundXmlDocument Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2787,7 +2775,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlDeclaration + Friend Partial Class BoundXmlDeclaration Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2801,7 +2789,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlProcessingInstruction + Friend Partial Class BoundXmlProcessingInstruction Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2815,7 +2803,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlComment + Friend Partial Class BoundXmlComment Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2829,7 +2817,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlAttribute + Friend Partial Class BoundXmlAttribute Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2843,7 +2831,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlElement + Friend Partial Class BoundXmlElement Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2857,7 +2845,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlMemberAccess + Friend Partial Class BoundXmlMemberAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2871,7 +2859,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlEmbeddedExpression + Friend Partial Class BoundXmlEmbeddedExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2885,7 +2873,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundXmlCData + Friend Partial Class BoundXmlCData Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2899,7 +2887,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundUnstructuredExceptionHandlingCatchFilter + Friend Partial Class BoundUnstructuredExceptionHandlingCatchFilter Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2913,7 +2901,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundSpillSequence + Friend Partial Class BoundSpillSequence Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2927,7 +2915,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMidResult + Friend Partial Class BoundMidResult Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2947,7 +2935,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundLoweredConditionalAccess + Friend Partial Class BoundLoweredConditionalAccess Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -2961,17 +2949,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundComplexConditionalAccessReceiver + Friend Partial Class BoundComplexConditionalAccessReceiver Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.ValueTypeReceiver, Me.ReferenceTypeReceiver) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2981,7 +2963,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundNameOfOperator + Friend Partial Class BoundNameOfOperator Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3001,7 +2983,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundTypeAsValueExpression + Friend Partial Class BoundTypeAsValueExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3021,7 +3003,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundInterpolatedStringExpression + Friend Partial Class BoundInterpolatedStringExpression Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3041,7 +3023,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundInterpolation + Friend Partial Class BoundInterpolation Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) Get Return ImmutableArray.Create(Of IOperation)(Me.Expression, Me.AlignmentOpt, Me.FormatStringOpt) @@ -3049,7 +3031,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Property End Class - Partial Friend Class BoundModuleVersionId + Friend Partial Class BoundModuleVersionId Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3063,7 +3045,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundModuleVersionIdString + Friend Partial Class BoundModuleVersionIdString Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3077,7 +3059,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMethodDefIndex + Friend Partial Class BoundMethodDefIndex Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3091,7 +3073,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundMaximumMethodDefIndex + Friend Partial Class BoundMaximumMethodDefIndex Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3105,7 +3087,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundInstrumentationPayloadRoot + Friend Partial Class BoundInstrumentationPayloadRoot Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function @@ -3119,7 +3101,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic End Function End Class - Partial Friend Class BoundSourceDocumentIndex + Friend Partial Class BoundSourceDocumentIndex Protected Overrides Function ExpressionKind() As OperationKind Return OperationKind.None End Function diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb index 907065cf462c7..37343a75c84db 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Statement.vb @@ -66,6 +66,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ''' ''' Override this property to return the child operations if the IOperation API corresponding to this bound node is not yet designed or implemented. ''' + ''' + ''' Note that any of the child operation nodes may be null. + ''' Protected Overridable ReadOnly Property Children As ImmutableArray(Of IOperation) Get Return ImmutableArray(Of IOperation).Empty @@ -1794,12 +1797,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.Condition) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1910,12 +1907,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return Me.Jumps.As(Of IOperation).Insert(0, Me.Value) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1930,12 +1921,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return Me.Jumps.As(Of IOperation).InsertRange(0, {Me.ResumeLabel, Me.ResumeNextLabel}) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub From 5f6e036c0b0d903cf029aca9b7752f33414bafb9 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Mon, 1 May 2017 15:52:23 -0700 Subject: [PATCH 070/214] Missed revert of one additional Children override for lowering specific bound node --- src/Compilers/CSharp/Portable/BoundTree/Expression.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index 804f839396268..d0404309151b9 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -2101,8 +2101,6 @@ internal partial class BoundFixedLocalCollectionInitializer { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); - public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); From 75c034f1ca9299bdba0315dfa96d59ca9cff0c04 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 19:27:58 -0700 Subject: [PATCH 071/214] Move more common code into the heleprs. --- .../AbstractUseExpressionBodyDiagnosticAnalyzer.cs | 6 ++---- .../UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs | 4 +--- ...UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs | 4 +--- ...essionBodyForConversionOperatorsDiagnosticAnalyzer.cs | 4 +--- .../UseExpressionBodyForIndexersDiagnosticAnalyzer.cs | 4 +--- .../UseExpressionBodyForMethodsDiagnosticAnalyzer.cs | 4 +--- .../UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs | 4 +--- .../UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs | 4 +--- .../AbstractUseExpressionBodyCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForAccessorsCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForConstructorsCodeFixProvider.cs | 3 +-- ...xpressionBodyForConversionOperatorsCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForIndexersCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForMethodsCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForOperatorsCodeFixProvider.cs | 3 +-- .../UseExpressionBodyForPropertiesCodeFixProvider.cs | 3 +-- .../Helpers/AbstractUseExpressionBodyHelper.cs | 9 ++++++++- .../Helpers/UseExpressionBodyForAccessorsHelper.cs | 8 ++++++-- .../Helpers/UseExpressionBodyForConstructorsHelper.cs | 8 ++++++-- .../UseExpressionBodyForConversionOperatorsHelper.cs | 8 ++++++-- .../Helpers/UseExpressionBodyForIndexersHelper.cs | 8 ++++++-- .../Helpers/UseExpressionBodyForMethodsHelper.cs | 8 ++++++-- .../Helpers/UseExpressionBodyForOperatorsHelper.cs | 8 ++++++-- .../Helpers/UseExpressionBodyForPropertiesHelper.cs | 8 ++++++-- 24 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs index d9e72f43d7e9d..bebf6e9b0efd3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs @@ -19,12 +19,10 @@ internal abstract class AbstractUseExpressionBodyDiagnosticAnalyzer _helper; protected AbstractUseExpressionBodyDiagnosticAnalyzer( - string diagnosticId, - ImmutableArray syntaxKinds, AbstractUseExpressionBodyHelper helper) - : base(diagnosticId, helper.UseExpressionBodyTitle) + : base(helper.DiagnosticId, helper.UseExpressionBodyTitle) { - _syntaxKinds = syntaxKinds; + _syntaxKinds = helper.SyntaxKinds; _helper = helper; } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs index 88bf61d3621d3..4afc624f95b51 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs @@ -14,9 +14,7 @@ internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); public UseExpressionBodyForAccessorsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration), - UseExpressionBodyForAccessorsHelper.Instance) + : base(UseExpressionBodyForAccessorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs index 2291a7ddb4704..ac01c6024e460 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForConstructorsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, - ImmutableArray.Create(SyntaxKind.ConstructorDeclaration), - UseExpressionBodyForConstructorsHelper.Instance) + : base(UseExpressionBodyForConstructorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs index d51a9e3c04215..750225421c103 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, - ImmutableArray.Create(SyntaxKind.ConversionOperatorDeclaration), - UseExpressionBodyForConversionOperatorsHelper.Instance) + : base(UseExpressionBodyForConversionOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs index c799acf60f807..13b651f30a5b4 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForIndexersDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForIndexersDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, - ImmutableArray.Create(SyntaxKind.IndexerDeclaration), - UseExpressionBodyForIndexersHelper.Instance) + : base(UseExpressionBodyForIndexersHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs index ba7a844c3d987..c42bc9c16d4c8 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForMethodsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - ImmutableArray.Create(SyntaxKind.MethodDeclaration), - UseExpressionBodyForMethodsHelper.Instance) + : base(UseExpressionBodyForMethodsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs index 80b6279472a86..48b68fb0f2b6c 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForOperatorsDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - ImmutableArray.Create(SyntaxKind.OperatorDeclaration), - UseExpressionBodyForOperatorsHelper.Instance) + : base(UseExpressionBodyForOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs index b2e98395f9ae6..ae8960caf08c7 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs @@ -11,9 +11,7 @@ internal class UseExpressionBodyForPropertiesDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { public UseExpressionBodyForPropertiesDiagnosticAnalyzer() - : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, - ImmutableArray.Create(SyntaxKind.PropertyDeclaration), - UseExpressionBodyForPropertiesHelper.Instance) + : base(UseExpressionBodyForPropertiesHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs index aa15476949d1a..015b4c78024cc 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs @@ -24,10 +24,9 @@ internal abstract partial class AbstractUseExpressionBodyCodeFixProvider FixableDiagnosticIds { get; } protected AbstractUseExpressionBodyCodeFixProvider( - string diagnosticId, AbstractUseExpressionBodyHelper helper) { - FixableDiagnosticIds = ImmutableArray.Create(diagnosticId); + FixableDiagnosticIds = ImmutableArray.Create(helper.DiagnosticId); _helper = helper; } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs index 473e07d6df744..591c369ad0818 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForAccessorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, - UseExpressionBodyForAccessorsHelper.Instance) + : base(UseExpressionBodyForAccessorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs index 73940a41f853d..481f5d0738bac 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForConstructorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, - UseExpressionBodyForConstructorsHelper.Instance) + : base(UseExpressionBodyForConstructorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs index 9e37730b66d3b..d8208e7905b46 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForConversionOperatorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, - UseExpressionBodyForConversionOperatorsHelper.Instance) + : base(UseExpressionBodyForConversionOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs index 3b9cec30ee235..18b2717e49732 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForIndexersCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, - UseExpressionBodyForIndexersHelper.Instance) + : base(UseExpressionBodyForIndexersHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs index 100f6b1161b8d..32c160f613e45 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForMethodsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, - UseExpressionBodyForMethodsHelper.Instance) + : base(UseExpressionBodyForMethodsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs index 41ddb16fbf04f..c98deed84010d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForOperatorsCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, - UseExpressionBodyForOperatorsHelper.Instance) + : base(UseExpressionBodyForOperatorsHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs index bd40019aaeda2..64dd42022b633 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider { public UseExpressionBodyForPropertiesCodeFixProvider() - : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, - UseExpressionBodyForPropertiesHelper.Instance) + : base(UseExpressionBodyForPropertiesHelper.Instance) { } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs index 0ef6f125eab4d..a560ae6e9aec3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.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.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CodeStyle; @@ -23,15 +24,21 @@ internal abstract class AbstractUseExpressionBodyHelper public readonly Option> Option; public readonly LocalizableString UseExpressionBodyTitle; public readonly LocalizableString UseBlockBodyTitle; + public readonly string DiagnosticId; + public readonly ImmutableArray SyntaxKinds; protected AbstractUseExpressionBodyHelper( + string diagnosticId, LocalizableString useExpressionBodyTitle, LocalizableString useBlockBodyTitle, - Option> option) + Option> option, + ImmutableArray syntaxKinds) { + DiagnosticId = diagnosticId; Option = option; UseExpressionBodyTitle = useExpressionBodyTitle; UseBlockBodyTitle = useBlockBodyTitle; + SyntaxKinds = syntaxKinds; } public abstract BlockSyntax GetBody(TDeclaration declaration); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index d9f0197ffe72a..0f30e14f4273e 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -1,8 +1,10 @@ // 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.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody @@ -24,9 +26,11 @@ internal class UseExpressionBodyForAccessorsHelper : private readonly Func _baseCanOfferUseBlockBody; private UseExpressionBodyForAccessorsHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedAccessors) + CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, + ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration)) { _baseCanOfferUseExpressionBody = base.CanOfferUseExpressionBody; _baseCanOfferUseBlockBody = base.CanOfferUseBlockBody; diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index 49c180ebfa28c..f46127794923d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -1,7 +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.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { @@ -11,9 +13,11 @@ internal class UseExpressionBodyForConstructorsHelper : public static readonly UseExpressionBodyForConstructorsHelper Instance = new UseExpressionBodyForConstructorsHelper(); private UseExpressionBodyForConstructorsHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForConstructorsDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_constructors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedConstructors) + CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, + ImmutableArray.Create(SyntaxKind.ConstructorDeclaration)) { } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index e248b5c718f53..46bc99ef14580 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -1,7 +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.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { @@ -11,9 +13,11 @@ internal class UseExpressionBodyForConversionOperatorsHelper : public static readonly UseExpressionBodyForConversionOperatorsHelper Instance = new UseExpressionBodyForConversionOperatorsHelper(); private UseExpressionBodyForConversionOperatorsHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForConversionOperatorsDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedOperators) + CSharpCodeStyleOptions.PreferExpressionBodiedOperators, + ImmutableArray.Create(SyntaxKind.ConversionOperatorDeclaration)) { } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs index 88736c927fdad..684eb20de8d50 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs @@ -1,8 +1,10 @@ // 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.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody @@ -13,9 +15,11 @@ internal class UseExpressionBodyForIndexersHelper : public static readonly UseExpressionBodyForIndexersHelper Instance = new UseExpressionBodyForIndexersHelper(); private UseExpressionBodyForIndexersHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForIndexersDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_indexers), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedIndexers) + CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, + ImmutableArray.Create(SyntaxKind.IndexerDeclaration)) { } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index 8925308b68f43..ca098aea9f1f6 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -1,8 +1,10 @@ // 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 Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { @@ -12,9 +14,11 @@ internal class UseExpressionBodyForMethodsHelper : public static readonly UseExpressionBodyForMethodsHelper Instance = new UseExpressionBodyForMethodsHelper(); private UseExpressionBodyForMethodsHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForMethodsDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_methods), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedMethods) + CSharpCodeStyleOptions.PreferExpressionBodiedMethods, + ImmutableArray.Create(SyntaxKind.MethodDeclaration)) { } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index a29d338c89f8a..4f67c6f7aefd3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -1,7 +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.Collections.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { @@ -11,9 +13,11 @@ internal class UseExpressionBodyForOperatorsHelper : public static readonly UseExpressionBodyForOperatorsHelper Instance = new UseExpressionBodyForOperatorsHelper(); private UseExpressionBodyForOperatorsHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForOperatorsDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_operators), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedOperators) + CSharpCodeStyleOptions.PreferExpressionBodiedOperators, + ImmutableArray.Create(SyntaxKind.OperatorDeclaration)) { } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 199e4ee9c2a3c..75325bc84b82c 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -1,8 +1,10 @@ // 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.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody @@ -13,9 +15,11 @@ internal class UseExpressionBodyForPropertiesHelper : public static readonly UseExpressionBodyForPropertiesHelper Instance = new UseExpressionBodyForPropertiesHelper(); private UseExpressionBodyForPropertiesHelper() - : base(new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), + : base(IDEDiagnosticIds.UseExpressionBodyForPropertiesDiagnosticId, + new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), new LocalizableResourceString(nameof(FeaturesResources.Use_block_body_for_properties), FeaturesResources.ResourceManager, typeof(FeaturesResources)), - CSharpCodeStyleOptions.PreferExpressionBodiedProperties) + CSharpCodeStyleOptions.PreferExpressionBodiedProperties, + ImmutableArray.Create(SyntaxKind.PropertyDeclaration)) { } From 8689b7eddfaa280b2eb71e713eb349fe646b48c1 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 19:35:55 -0700 Subject: [PATCH 072/214] Remove unnecessary fields. --- .../UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs index 4afc624f95b51..5ce7dd09caa21 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs @@ -10,9 +10,6 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : AbstractUseExpressionBodyDiagnosticAnalyzer { - private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); - private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); - public UseExpressionBodyForAccessorsDiagnosticAnalyzer() : base(UseExpressionBodyForAccessorsHelper.Instance) { From 2eb7451f7d28f87dcd70146f6b73d694ea66c196 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:07:26 -0700 Subject: [PATCH 073/214] Simplify implementation of UseExpressionBody. --- ...ExpressionBodyForAccessorsAnalyzerTests.cs | 23 ++-- ...ressionBodyForConstructorsAnalyzerTests.cs | 3 +- ...BodyForConversionOperatorsAnalyzerTests.cs | 3 +- ...eExpressionBodyForIndexersAnalyzerTests.cs | 2 +- ...seExpressionBodyForMethodsAnalyzerTests.cs | 2 +- ...ExpressionBodyForOperatorsAnalyzerTests.cs | 3 +- ...xpressionBodyForPropertiesAnalyzerTests.cs | 2 +- ...ressionBodyForAccessorsRefactoringTests.cs | 18 ++- ...sionBodyForConstructorsRefactoringTests.cs | 2 +- ...yForConversionOperatorsRefactoringTests.cs | 2 +- ...pressionBodyForIndexersRefactoringTests.cs | 2 +- ...xpressionBodyForMethodsRefactoringTests.cs | 2 +- ...ressionBodyForOperatorsRefactoringTests.cs | 2 +- ...essionBodyForPropertiesRefactoringTests.cs | 13 ++- ...ractUseExpressionBodyDiagnosticAnalyzer.cs | 71 ++++++++---- ...ssionBodyForAccessorsDiagnosticAnalyzer.cs | 32 +++--- ...onBodyForConstructorsDiagnosticAnalyzer.cs | 32 +++--- ...orConversionOperatorsDiagnosticAnalyzer.cs | 32 +++--- ...essionBodyForIndexersDiagnosticAnalyzer.cs | 32 +++--- ...ressionBodyForMethodsDiagnosticAnalyzer.cs | 32 +++--- ...ssionBodyForOperatorsDiagnosticAnalyzer.cs | 32 +++--- ...sionBodyForPropertiesDiagnosticAnalyzer.cs | 32 +++--- ...bstractUseExpressionBodyCodeFixProvider.cs | 22 ++-- ...pressionBodyForAccessorsCodeFixProvider.cs | 32 +++--- ...ssionBodyForConstructorsCodeFixProvider.cs | 32 +++--- ...dyForConversionOperatorsCodeFixProvider.cs | 32 +++--- ...xpressionBodyForIndexersCodeFixProvider.cs | 32 +++--- ...ExpressionBodyForMethodsCodeFixProvider.cs | 32 +++--- ...pressionBodyForOperatorsCodeFixProvider.cs | 32 +++--- ...ressionBodyForPropertiesCodeFixProvider.cs | 32 +++--- ...seExpressionBodyCodeRefactoringProvider.cs | 74 ++++++++---- ...BodyForAccessorsCodeRefactoringProvider.cs | 30 ++--- ...yForConstructorsCodeRefactoringProvider.cs | 30 ++--- ...versionOperatorsCodeRefactoringProvider.cs | 30 ++--- ...nBodyForIndexersCodeRefactoringProvider.cs | 30 ++--- ...onBodyForMethodsCodeRefactoringProvider.cs | 30 ++--- ...BodyForOperatorsCodeRefactoringProvider.cs | 30 ++--- ...odyForPropertiesCodeRefactoringProvider.cs | 30 ++--- .../AbstractUseExpressionBodyHelper.cs | 60 ++++++++-- .../UseExpressionBodyForAccessorsHelper.cs | 107 +++++++++--------- .../UseExpressionBodyForConstructorsHelper.cs | 4 +- ...ressionBodyForConversionOperatorsHelper.cs | 4 +- .../UseExpressionBodyForIndexersHelper.cs | 4 +- .../UseExpressionBodyForMethodsHelper.cs | 4 +- .../UseExpressionBodyForOperatorsHelper.cs | 4 +- .../UseExpressionBodyForPropertiesHelper.cs | 4 +- .../AbstractCodeStyleDiagnosticAnalyzer.cs | 4 +- 47 files changed, 611 insertions(+), 488 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs index 14d4a31715653..01f67442394cf 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs @@ -18,8 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForAccessorsTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForAccessorsDiagnosticAnalyzer(), - new UseExpressionBodyForAccessorsCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => OptionsSet( @@ -60,9 +59,9 @@ int Foo } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestMissingIfPropertyIsOn() + public async Task TestUpdatePropertyInsteadOfAccessor() { - await TestMissingInRegularAndScriptAsync( + await TestInRegularAndScript1Async( @"class C { int Foo @@ -72,7 +71,11 @@ int Foo [|return|] Bar(); } } -}", new TestParameters(options: UseExpressionBodyIncludingPropertiesAndIndexers)); +}", +@"class C +{ + int Foo => Bar(); +}", parameters: new TestParameters(options: UseExpressionBodyIncludingPropertiesAndIndexers)); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] @@ -99,9 +102,9 @@ int this[int i] } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestMissingIfIndexerIsOn() + public async Task TestUpdateIndexerIfIndexerAndAccessorCanBeUpdated() { - await TestMissingInRegularAndScriptAsync( + await TestInRegularAndScript1Async( @"class C { int this[int i] @@ -111,7 +114,11 @@ int this[int i] [|return|] Bar(); } } -}", new TestParameters(options: UseExpressionBodyIncludingPropertiesAndIndexers)); +}", +@"class C +{ + int this[int i] => Bar(); +}", parameters: new TestParameters(options: UseExpressionBodyIncludingPropertiesAndIndexers)); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs index 45c0ce53b6765..b4a82cbdc90de 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConstructorsAnalyzerTests.cs @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForConstructorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForConstructorsDiagnosticAnalyzer(), - new UseExpressionBodyForConstructorsCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => Option(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs index fd9065976eeeb..87faef0c1780c 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForConversionOperatorsAnalyzerTests.cs @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForConversionOperatorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer(), - new UseExpressionBodyForConversionOperatorsCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs index 944961d0d45d5..7fad114993704 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForIndexersAnalyzerTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForIndexersAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForIndexersDiagnosticAnalyzer(), new UseExpressionBodyForIndexersCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => OptionsSet( diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs index 780cdc1ffb8e3..a7204a1f19e08 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForMethodsAnalyzerTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForMethodsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForMethodsDiagnosticAnalyzer(), new UseExpressionBodyForMethodsCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs index 6fc8e0842844c..0b622cf69e3b7 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForOperatorsAnalyzerTests.cs @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForOperatorsAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForOperatorsDiagnosticAnalyzer(), - new UseExpressionBodyForOperatorsCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs index a904ff63bd61b..59bee66a86ebd 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs @@ -17,7 +17,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForPropertiesAnalyzerTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - => (new UseExpressionBodyForPropertiesDiagnosticAnalyzer(), new UseExpressionBodyForPropertiesCodeFixProvider()); + => (new UseExpressionBodyDiagnosticAnalyzer(), new UseExpressionBodyCodeFixProvider()); private IDictionary UseExpressionBody => OptionsSet( diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs index 9ecce46988a58..0d3ed2d76ff01 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForAccessorsRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForAccessorsCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBodyForAccessors_BlockBodyForProperties => OptionsSet( @@ -38,9 +38,9 @@ protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspa this.SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithNoneEnforcement)); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody() + public async Task TestUpdatePropertyIfPropertyWantsBlockAndAccesorWantsExpression() { - await TestMissingAsync( + await TestInRegularAndScript1Async( @"class C { int Foo @@ -50,6 +50,10 @@ int Foo [||]return Bar(); } } +}", +@"class C +{ + int Foo => Bar(); }", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } @@ -90,9 +94,9 @@ int Foo } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInBlockBody2() + public async Task TestOfferExpressionBodyForPropertyIfPropertyAndAccessorBothPreferExpressions() { - await TestMissingAsync( + await TestInRegularAndScript1Async( @"class C { int Foo @@ -102,6 +106,10 @@ int Foo return [||]Bar(); } } +}", +@"class C +{ + int Foo => [||]Bar(); }", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs index 98ffe015b69f1..45db214ff5557 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConstructorsRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForConstructorsRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForConstructorsCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedConstructors, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs index 5604e61c426bd..9f033cf4536e1 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForConversionOperatorsRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForConversionOperatorsRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForConversionOperatorsCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs index 61c669dfc3b09..5941b5f57b714 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForIndexersRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForIndexersRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForIndexersCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs index acaa144327a78..1c49ca1f1af05 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForMethodsRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForMethodsRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForMethodsCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs index 41287510c9826..93e0e4e32453c 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForOperatorsRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForOperatorsRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForOperatorsCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBody => this.Option(CSharpCodeStyleOptions.PreferExpressionBodiedOperators, CSharpCodeStyleOptions.WhenPossibleWithNoneEnforcement); diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs index b8245440f734a..159586a930e42 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForPropertiesRefactoringTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.UseExpressionBody public class UseExpressionBodyForPropertiesRefactoringTests : AbstractCSharpCodeActionTest { protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters) - => new UseExpressionBodyForPropertiesCodeRefactoringProvider(); + => new UseExpressionBodyCodeRefactoringProvider(); private IDictionary UseExpressionBodyForAccessors_BlockBodyForProperties => OptionsSet( @@ -54,9 +54,9 @@ int Foo } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersExpressionBodiesAndInBlockBody2() + public async Task TestUpdateAccessorIfAccessWantsBlockAndPropertyWantsExpression() { - await TestMissingAsync( + await TestInRegularAndScript1Async( @"class C { int Foo @@ -66,6 +66,13 @@ int Foo [||]return Bar(); } } +}", +@"class C +{ + int Foo + { + get => Bar(); + } }", parameters: new TestParameters(options: UseBlockBodyForAccessors_ExpressionBodyForProperties)); } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs index bebf6e9b0efd3..a51c3e5b65f0d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs @@ -2,28 +2,31 @@ using System; using System.Collections.Immutable; +using System.Linq; using Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - internal abstract class AbstractUseExpressionBodyDiagnosticAnalyzer : - AbstractCodeStyleDiagnosticAnalyzer - where TDeclaration : SyntaxNode + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal class UseExpressionBodyDiagnosticAnalyzer : AbstractCodeStyleDiagnosticAnalyzer { private readonly ImmutableArray _syntaxKinds; + public override ImmutableArray SupportedDiagnostics { get; } + public override bool OpenFileOnly(Workspace workspace) => false; - private readonly AbstractUseExpressionBodyHelper _helper; + private static readonly ImmutableArray _helpers = UseExpressionBodyHelper.Helpers; - protected AbstractUseExpressionBodyDiagnosticAnalyzer( - AbstractUseExpressionBodyHelper helper) - : base(helper.DiagnosticId, helper.UseExpressionBodyTitle) + public UseExpressionBodyDiagnosticAnalyzer() + : base(_helpers[0].DiagnosticId, _helpers[0].UseExpressionBodyTitle) { - _syntaxKinds = helper.SyntaxKinds; - _helper = helper; + _syntaxKinds = _helpers.SelectMany(h => h.SyntaxKinds).ToImmutableArray(); + SupportedDiagnostics = _helpers.SelectAsArray( + h => CreateDescriptorWithId(h.DiagnosticId, h.UseExpressionBodyTitle, h.UseExpressionBodyTitle, DiagnosticSeverity.Hidden)); } public override DiagnosticAnalyzerCategory GetAnalyzerCategory() => DiagnosticAnalyzerCategory.SemanticDocumentAnalysis; @@ -42,41 +45,69 @@ private void AnalyzeSyntax(SyntaxNodeAnalysisContext context) return; } - var diagnostic = AnalyzeSyntax(optionSet, (TDeclaration)context.Node); - if (diagnostic != null) + var nodeKind = context.Node.Kind(); + + // Don't offer a fix on an accessor, if we would also offer it on the property/indexer. + if (UseExpressionBodyForAccessorsHelper.Instance.SyntaxKinds.Contains(nodeKind)) + { + var grandparent = context.Node.Parent.Parent; + + if (grandparent.Kind() == SyntaxKind.PropertyDeclaration && + AnalyzeSyntax(optionSet, grandparent, UseExpressionBodyForPropertiesHelper.Instance) != null) + { + return; + } + + if (grandparent.Kind() == SyntaxKind.IndexerDeclaration && + AnalyzeSyntax(optionSet, grandparent, UseExpressionBodyForIndexersHelper.Instance) != null) + { + return; + } + } + + foreach (var helper in _helpers) { - context.ReportDiagnostic(diagnostic); + if (helper.SyntaxKinds.Contains(nodeKind)) + { + var diagnostic = AnalyzeSyntax(optionSet, context.Node, helper); + if (diagnostic != null) + { + context.ReportDiagnostic(diagnostic); + return; + } + } } } - private Diagnostic AnalyzeSyntax(OptionSet optionSet, TDeclaration declaration) + private Diagnostic AnalyzeSyntax( + OptionSet optionSet, SyntaxNode declaration, UseExpressionBodyHelper helper) { - var preferExpressionBodiedOption = optionSet.GetOption(_helper.Option); + var preferExpressionBodiedOption = optionSet.GetOption(helper.Option); var severity = preferExpressionBodiedOption.Notification.Value; - if (_helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: true)) + if (helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: true)) { var location = severity == DiagnosticSeverity.Hidden ? declaration.GetLocation() - : _helper.GetBody(declaration).Statements[0].GetLocation(); + : helper.GetBody(declaration).Statements[0].GetLocation(); var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); return Diagnostic.Create( - CreateDescriptorWithTitle(_helper.UseExpressionBodyTitle, severity, GetCustomTags(severity)), + CreateDescriptorWithId(helper.DiagnosticId, helper.UseExpressionBodyTitle, helper.UseExpressionBodyTitle, severity, GetCustomTags(severity)), location, additionalLocations: additionalLocations); } - if (_helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: true)) + if (helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: true)) { // They have an expression body. Create a diagnostic to conver it to a block // if they don't want expression bodies for this member. var location = severity == DiagnosticSeverity.Hidden ? declaration.GetLocation() - : _helper.GetExpressionBody(declaration).GetLocation(); + : helper.GetExpressionBody(declaration).GetLocation(); var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); return Diagnostic.Create( - CreateDescriptorWithTitle(_helper.UseBlockBodyTitle, severity, GetCustomTags(severity)), + CreateDescriptorWithId(helper.DiagnosticId, helper.UseBlockBodyTitle, helper.UseBlockBodyTitle, severity, GetCustomTags(severity)), location, additionalLocations: additionalLocations); } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs index 5ce7dd09caa21..bf900fdb22a21 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForAccessorsDiagnosticAnalyzer() - : base(UseExpressionBodyForAccessorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForAccessorsDiagnosticAnalyzer() +// : base(UseExpressionBodyForAccessorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs index ac01c6024e460..a25d297d33f0f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForConstructorsDiagnosticAnalyzer() - : base(UseExpressionBodyForConstructorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForConstructorsDiagnosticAnalyzer() +// : base(UseExpressionBodyForConstructorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs index 750225421c103..baaf8d5cc119a 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() - : base(UseExpressionBodyForConversionOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() +// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs index 13b651f30a5b4..8334feedb0ced 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForIndexersDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForIndexersDiagnosticAnalyzer() - : base(UseExpressionBodyForIndexersHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForIndexersDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForIndexersDiagnosticAnalyzer() +// : base(UseExpressionBodyForIndexersHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs index c42bc9c16d4c8..b47832a8d8556 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForMethodsDiagnosticAnalyzer() - : base(UseExpressionBodyForMethodsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForMethodsDiagnosticAnalyzer() +// : base(UseExpressionBodyForMethodsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs index 48b68fb0f2b6c..cd81b7cd95d35 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForOperatorsDiagnosticAnalyzer() - : base(UseExpressionBodyForOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForOperatorsDiagnosticAnalyzer() +// : base(UseExpressionBodyForOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs index ae8960caf08c7..ce1e300da3b45 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Collections.Immutable; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal class UseExpressionBodyForPropertiesDiagnosticAnalyzer : - AbstractUseExpressionBodyDiagnosticAnalyzer - { - public UseExpressionBodyForPropertiesDiagnosticAnalyzer() - : base(UseExpressionBodyForPropertiesHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [DiagnosticAnalyzer(LanguageNames.CSharp)] +// internal class UseExpressionBodyForPropertiesDiagnosticAnalyzer : +// AbstractUseExpressionBodyDiagnosticAnalyzer +// { +// public UseExpressionBodyForPropertiesDiagnosticAnalyzer() +// : base(UseExpressionBodyForPropertiesHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs index 015b4c78024cc..314a305b94129 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Immutable; +using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -15,19 +16,17 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - internal abstract partial class AbstractUseExpressionBodyCodeFixProvider : + [ExportCodeFixProvider(LanguageNames.CSharp), Shared] + internal partial class UseExpressionBodyCodeFixProvider : SyntaxEditorBasedCodeFixProvider - where TDeclaration : SyntaxNode { - private readonly AbstractUseExpressionBodyHelper _helper; - public sealed override ImmutableArray FixableDiagnosticIds { get; } - protected AbstractUseExpressionBodyCodeFixProvider( - AbstractUseExpressionBodyHelper helper) + private static readonly ImmutableArray _helpers = UseExpressionBodyHelper.Helpers; + + public UseExpressionBodyCodeFixProvider() { - FixableDiagnosticIds = ImmutableArray.Create(helper.DiagnosticId); - _helper = helper; + FixableDiagnosticIds = _helpers.SelectAsArray(h => h.DiagnosticId); } protected override bool IncludeDiagnosticDuringFixAll(Diagnostic diagnostic) @@ -65,10 +64,11 @@ private void AddEdits( OptionSet options, CancellationToken cancellationToken) { var declarationLocation = diagnostic.AdditionalLocations[0]; - var declaration = (TDeclaration)declarationLocation.FindNode(cancellationToken); + var helper = _helpers.Single(h => h.DiagnosticId == diagnostic.Id); + var declaration = declarationLocation.FindNode(cancellationToken); - var updatedDeclaration = _helper.Update(declaration, options) - .WithAdditionalAnnotations(Formatter.Annotation); + var updatedDeclaration = helper.Update(declaration, options) + .WithAdditionalAnnotations(Formatter.Annotation); editor.ReplaceNode(declaration, updatedDeclaration); } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs index 591c369ad0818..6d25637b09805 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForAccessorsCodeFixProvider() - : base(UseExpressionBodyForAccessorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForAccessorsCodeFixProvider() +// : base(UseExpressionBodyForAccessorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs index 481f5d0738bac..75eb89fc8e3fc 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForConstructorsCodeFixProvider() - : base(UseExpressionBodyForConstructorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForConstructorsCodeFixProvider() +// : base(UseExpressionBodyForConstructorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs index d8208e7905b46..39dbb32c56497 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForConversionOperatorsCodeFixProvider() - : base(UseExpressionBodyForConversionOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForConversionOperatorsCodeFixProvider() +// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs index 18b2717e49732..ab7a90aa39cd1 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForIndexersCodeFixProvider() - : base(UseExpressionBodyForIndexersHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForIndexersCodeFixProvider() +// : base(UseExpressionBodyForIndexersHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs index 32c160f613e45..144f12eae6bd2 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForMethodsCodeFixProvider() - : base(UseExpressionBodyForMethodsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForMethodsCodeFixProvider() +// : base(UseExpressionBodyForMethodsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs index c98deed84010d..5e749f08ffa56 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForOperatorsCodeFixProvider() - : base(UseExpressionBodyForOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForOperatorsCodeFixProvider() +// : base(UseExpressionBodyForOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs index 64dd42022b633..6a5fb9cbef00d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs @@ -1,18 +1,18 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeFixes; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeFixes; +//using Microsoft.CodeAnalysis.CSharp.Syntax; +//using Microsoft.CodeAnalysis.Diagnostics; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider - { - public UseExpressionBodyForPropertiesCodeFixProvider() - : base(UseExpressionBodyForPropertiesHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider +// { +// public UseExpressionBodyForPropertiesCodeFixProvider() +// : base(UseExpressionBodyForPropertiesHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs index 481ef4bac751f..f1506178e53ed 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Immutable; +using System.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -14,16 +15,13 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - internal abstract class AbstractUseExpressionBodyCodeRefactoringProvider : - CodeRefactoringProvider - where TDeclaration : SyntaxNode + [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] + internal class UseExpressionBodyCodeRefactoringProvider : CodeRefactoringProvider { - private readonly AbstractUseExpressionBodyHelper _helper; + private static readonly ImmutableArray _helpers = UseExpressionBodyHelper.Helpers; - protected AbstractUseExpressionBodyCodeRefactoringProvider( - AbstractUseExpressionBodyHelper helper) + public UseExpressionBodyCodeRefactoringProvider() { - _helper = helper; } public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) @@ -54,35 +52,71 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte return; } - var declaration = node.FirstAncestorOrSelf(); + var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + + foreach (var helper in _helpers) + { + var succeeded = TryComputeRefactoring(context, root, node, optionSet, helper); + if (succeeded) + { + return; + } + } + } + + private bool TryComputeRefactoring( + CodeRefactoringContext context, + SyntaxNode root, SyntaxNode node, OptionSet optionSet, + UseExpressionBodyHelper helper) + { + var declaration = GetDeclaration(node, helper); if (declaration == null) { - return; + return false; } - var optionSet = await document.GetOptionsAsync(cancellationToken).ConfigureAwait(false); + var document = context.Document; - if (_helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: false)) + bool succeeded = false; + if (helper.CanOfferUseExpressionBody(optionSet, declaration, forAnalyzer: false)) { context.RegisterRefactoring(new MyCodeAction( - _helper.UseExpressionBodyTitle.ToString(), - c => UpdateDocumentAsync(document, root, declaration, optionSet, c))); + helper.UseExpressionBodyTitle.ToString(), + c => UpdateDocumentAsync(document, root, declaration, optionSet, helper, c))); + succeeded = true; } - if (_helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: false)) + if (helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: false)) { context.RegisterRefactoring(new MyCodeAction( - _helper.UseBlockBodyTitle.ToString(), - c => UpdateDocumentAsync(document, root, declaration, optionSet, c))); + helper.UseBlockBodyTitle.ToString(), + c => UpdateDocumentAsync(document, root, declaration, optionSet, helper, c))); + succeeded = true; + } + + return succeeded; + } + + private SyntaxNode GetDeclaration(SyntaxNode node, UseExpressionBodyHelper helper) + { + for (var current = node; current != null; current = current.Parent) + { + if (helper.SyntaxKinds.Contains(current.Kind())) + { + return current; + } } + + return null; } private Task UpdateDocumentAsync( - Document document, SyntaxNode root, TDeclaration declaration, - DocumentOptionSet options, CancellationToken cancellationToken) + Document document, SyntaxNode root, SyntaxNode declaration, + OptionSet options, UseExpressionBodyHelper helper, + CancellationToken cancellationToken) { - var updatedDeclaration = _helper.Update(declaration, options) - .WithAdditionalAnnotations(Formatter.Annotation); + var updatedDeclaration = helper.Update(declaration, options) + .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(declaration, updatedDeclaration); return Task.FromResult(document.WithSyntaxRoot(newRoot)); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs index d2c5e476cc133..09c62c6115447 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForAccessorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForAccessorsCodeRefactoringProvider() - : base(UseExpressionBodyForAccessorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForAccessorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForAccessorsCodeRefactoringProvider() +// : base(UseExpressionBodyForAccessorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs index 3a5dc6d5bef45..849814e1576b3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConstructorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForConstructorsCodeRefactoringProvider() - : base(UseExpressionBodyForConstructorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForConstructorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForConstructorsCodeRefactoringProvider() +// : base(UseExpressionBodyForConstructorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs index f6af26dd3ff1f..f9db715e8fa2f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForConversionOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForConversionOperatorsCodeRefactoringProvider() - : base(UseExpressionBodyForConversionOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForConversionOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForConversionOperatorsCodeRefactoringProvider() +// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs index 1e8a031124541..3e7ba411db9ed 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForIndexersCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForIndexersCodeRefactoringProvider() - : base(UseExpressionBodyForIndexersHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForIndexersCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForIndexersCodeRefactoringProvider() +// : base(UseExpressionBodyForIndexersHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs index e41a6f2dd2aa9..78f633838e03f 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForMethodsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForMethodsCodeRefactoringProvider() - : base(UseExpressionBodyForMethodsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForMethodsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForMethodsCodeRefactoringProvider() +// : base(UseExpressionBodyForMethodsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs index 58416bfc40bb3..45fbcaf0062a3 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForOperatorsCodeRefactoringProvider() - : base(UseExpressionBodyForOperatorsHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForOperatorsCodeRefactoringProvider() +// : base(UseExpressionBodyForOperatorsHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs index 041fcebf2ae67..9bd3f36e3d0ee 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs @@ -1,17 +1,17 @@ -// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +//// 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.Composition; -using Microsoft.CodeAnalysis.CodeRefactorings; -using Microsoft.CodeAnalysis.CSharp.Syntax; +//using System.Composition; +//using Microsoft.CodeAnalysis.CodeRefactorings; +//using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -{ - [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] - internal class UseExpressionBodyForPropertiesCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider - { - public UseExpressionBodyForPropertiesCodeRefactoringProvider() - : base(UseExpressionBodyForPropertiesHelper.Instance) - { - } - } -} \ No newline at end of file +//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +//{ +// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] +// internal class UseExpressionBodyForPropertiesCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider +// { +// public UseExpressionBodyForPropertiesCodeRefactoringProvider() +// : base(UseExpressionBodyForPropertiesHelper.Instance) +// { +// } +// } +//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs index a560ae6e9aec3..30548e6b74365 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs @@ -13,19 +13,45 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { + internal abstract class UseExpressionBodyHelper + { + public abstract Option> Option { get; } + public abstract LocalizableString UseExpressionBodyTitle { get; } + public abstract LocalizableString UseBlockBodyTitle { get; } + public abstract string DiagnosticId { get; } + public abstract ImmutableArray SyntaxKinds { get; } + + public abstract BlockSyntax GetBody(SyntaxNode declaration); + public abstract ArrowExpressionClauseSyntax GetExpressionBody(SyntaxNode declaration); + + public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); + public abstract bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); + public abstract SyntaxNode Update(SyntaxNode declaration, OptionSet options); + + public static readonly ImmutableArray Helpers = + ImmutableArray.Create( + UseExpressionBodyForConstructorsHelper.Instance, + UseExpressionBodyForConversionOperatorsHelper.Instance, + UseExpressionBodyForIndexersHelper.Instance, + UseExpressionBodyForMethodsHelper.Instance, + UseExpressionBodyForOperatorsHelper.Instance, + UseExpressionBodyForPropertiesHelper.Instance, + UseExpressionBodyForAccessorsHelper.Instance); + } + /// /// Helper class that allows us to share lots of logic between the diagnostic analyzer and the /// code refactoring provider. Those can't share a common base class due to their own inheritance /// requirements with and . /// - internal abstract class AbstractUseExpressionBodyHelper + internal abstract class AbstractUseExpressionBodyHelper : UseExpressionBodyHelper where TDeclaration : SyntaxNode { - public readonly Option> Option; - public readonly LocalizableString UseExpressionBodyTitle; - public readonly LocalizableString UseBlockBodyTitle; - public readonly string DiagnosticId; - public readonly ImmutableArray SyntaxKinds; + public override Option> Option { get; } + public override LocalizableString UseExpressionBodyTitle { get; } + public override LocalizableString UseBlockBodyTitle { get; } + public override string DiagnosticId { get; } + public override ImmutableArray SyntaxKinds { get; } protected AbstractUseExpressionBodyHelper( string diagnosticId, @@ -41,9 +67,6 @@ protected AbstractUseExpressionBodyHelper( SyntaxKinds = syntaxKinds; } - public abstract BlockSyntax GetBody(TDeclaration declaration); - public abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration); - protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList) { if (accessorList != null && @@ -57,6 +80,21 @@ protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax acc return null; } + public override BlockSyntax GetBody(SyntaxNode declaration) + => GetBody((TDeclaration)declaration); + + public override ArrowExpressionClauseSyntax GetExpressionBody(SyntaxNode declaration) + => GetExpressionBody((TDeclaration)declaration); + + public override bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer) + => CanOfferUseExpressionBody(optionSet, (TDeclaration)declaration, forAnalyzer); + + public override bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer) + => CanOfferUseBlockBody(optionSet, (TDeclaration)declaration, forAnalyzer); + + public override SyntaxNode Update(SyntaxNode declaration, OptionSet options) + => Update((TDeclaration)declaration, options); + public virtual bool CanOfferUseExpressionBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { @@ -135,6 +173,10 @@ public TDeclaration Update(TDeclaration declaration, OptionSet options) } } + protected abstract BlockSyntax GetBody(TDeclaration declaration); + + protected abstract ArrowExpressionClauseSyntax GetExpressionBody(TDeclaration declaration); + protected abstract bool CreateReturnStatementForExpression(TDeclaration declaration); protected abstract SyntaxToken GetSemicolonToken(TDeclaration declaration); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 0f30e14f4273e..e797064836315 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -14,16 +14,13 @@ internal class UseExpressionBodyForAccessorsHelper : { public static readonly UseExpressionBodyForAccessorsHelper Instance = new UseExpressionBodyForAccessorsHelper(); - private readonly UseExpressionBodyForPropertiesDiagnosticAnalyzer propertyAnalyzer = new UseExpressionBodyForPropertiesDiagnosticAnalyzer(); - private readonly UseExpressionBodyForIndexersDiagnosticAnalyzer indexerAnalyzer = new UseExpressionBodyForIndexersDiagnosticAnalyzer(); + //private static readonly Func _propertyCanOfferUseExpressionBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; + //private static readonly Func _propertyCanOfferUseBlockBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; + //private static readonly Func _indexerCanOfferUseExpressionBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; + //private static readonly Func _indexerCanOfferUseBlockBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; - private static readonly Func _propertyCanOfferUseExpressionBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; - private static readonly Func _propertyCanOfferUseBlockBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; - private static readonly Func _indexerCanOfferUseExpressionBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; - private static readonly Func _indexerCanOfferUseBlockBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; - - private readonly Func _baseCanOfferUseExpressionBody; - private readonly Func _baseCanOfferUseBlockBody; + //private readonly Func _baseCanOfferUseExpressionBody; + //private readonly Func _baseCanOfferUseBlockBody; private UseExpressionBodyForAccessorsHelper() : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, @@ -32,58 +29,58 @@ private UseExpressionBodyForAccessorsHelper() CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration)) { - _baseCanOfferUseExpressionBody = base.CanOfferUseExpressionBody; - _baseCanOfferUseBlockBody = base.CanOfferUseBlockBody; + //_baseCanOfferUseExpressionBody = base.CanOfferUseExpressionBody; + //_baseCanOfferUseBlockBody = base.CanOfferUseBlockBody; } - public override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) + protected override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) => declaration.Body; - public override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) => declaration.ExpressionBody; - private bool CanOffer( - OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer, - Func propertyPredicate, - Func indexerPredicate, - Func basePredicate) - { - var grandParent = accessor.Parent.Parent; - - if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) - { - var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; - if (propertyPredicate(optionSet, propertyDeclaration, forAnalyzer)) - { - return false; - } - } - else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) - { - var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; - if (indexerPredicate(optionSet, indexerDeclaration, forAnalyzer)) - { - return false; - } - } - - return basePredicate(optionSet, accessor, forAnalyzer); - } - - public override bool CanOfferUseExpressionBody(OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) - { - return CanOffer( - optionSet, accessor, forAnalyzer, - _propertyCanOfferUseExpressionBody, _indexerCanOfferUseExpressionBody, _baseCanOfferUseExpressionBody); - } - - public override bool CanOfferUseBlockBody( - OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) - { - return CanOffer( - optionSet, accessor, forAnalyzer, - _propertyCanOfferUseBlockBody, _indexerCanOfferUseBlockBody, _baseCanOfferUseBlockBody); - } + //private bool CanOffer( + // OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer, + // Func propertyPredicate, + // Func indexerPredicate, + // Func basePredicate) + //{ + // var grandParent = accessor.Parent.Parent; + + // if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) + // { + // var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; + // if (propertyPredicate(optionSet, propertyDeclaration, forAnalyzer)) + // { + // return false; + // } + // } + // else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) + // { + // var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; + // if (indexerPredicate(optionSet, indexerDeclaration, forAnalyzer)) + // { + // return false; + // } + // } + + // return basePredicate(optionSet, accessor, forAnalyzer); + //} + + //public override bool CanOfferUseExpressionBody(OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + //{ + // return CanOffer( + // optionSet, accessor, forAnalyzer, + // _propertyCanOfferUseExpressionBody, _indexerCanOfferUseExpressionBody, _baseCanOfferUseExpressionBody); + //} + + //public override bool CanOfferUseBlockBody( + // OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) + //{ + // return CanOffer( + // optionSet, accessor, forAnalyzer, + // _propertyCanOfferUseBlockBody, _indexerCanOfferUseBlockBody, _baseCanOfferUseBlockBody); + //} protected override SyntaxToken GetSemicolonToken(AccessorDeclarationSyntax declaration) => declaration.SemicolonToken; diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index f46127794923d..306183b0c79e9 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -21,10 +21,10 @@ private UseExpressionBodyForConstructorsHelper() { } - public override BlockSyntax GetBody(ConstructorDeclarationSyntax declaration) + protected override BlockSyntax GetBody(ConstructorDeclarationSyntax declaration) => declaration.Body; - public override ArrowExpressionClauseSyntax GetExpressionBody(ConstructorDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(ConstructorDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(ConstructorDeclarationSyntax declaration) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index 46bc99ef14580..56eca18c54d91 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -21,10 +21,10 @@ private UseExpressionBodyForConversionOperatorsHelper() { } - public override BlockSyntax GetBody(ConversionOperatorDeclarationSyntax declaration) + protected override BlockSyntax GetBody(ConversionOperatorDeclarationSyntax declaration) => declaration.Body; - public override ArrowExpressionClauseSyntax GetExpressionBody(ConversionOperatorDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(ConversionOperatorDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(ConversionOperatorDeclarationSyntax declaration) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs index 684eb20de8d50..9c86b3ddfd1aa 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs @@ -23,10 +23,10 @@ private UseExpressionBodyForIndexersHelper() { } - public override BlockSyntax GetBody(IndexerDeclarationSyntax declaration) + protected override BlockSyntax GetBody(IndexerDeclarationSyntax declaration) => GetBodyFromSingleGetAccessor(declaration.AccessorList); - public override ArrowExpressionClauseSyntax GetExpressionBody(IndexerDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(IndexerDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(IndexerDeclarationSyntax declaration) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index ca098aea9f1f6..162252c6ea1bf 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -22,10 +22,10 @@ private UseExpressionBodyForMethodsHelper() { } - public override BlockSyntax GetBody(MethodDeclarationSyntax declaration) + protected override BlockSyntax GetBody(MethodDeclarationSyntax declaration) => declaration.Body; - public override ArrowExpressionClauseSyntax GetExpressionBody(MethodDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(MethodDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(MethodDeclarationSyntax declaration) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index 4f67c6f7aefd3..4f4a076d51e1a 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -21,10 +21,10 @@ private UseExpressionBodyForOperatorsHelper() { } - public override BlockSyntax GetBody(OperatorDeclarationSyntax declaration) + protected override BlockSyntax GetBody(OperatorDeclarationSyntax declaration) => declaration.Body; - public override ArrowExpressionClauseSyntax GetExpressionBody(OperatorDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(OperatorDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(OperatorDeclarationSyntax declaration) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 75325bc84b82c..3379d67d51b3b 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -23,10 +23,10 @@ private UseExpressionBodyForPropertiesHelper() { } - public override BlockSyntax GetBody(PropertyDeclarationSyntax declaration) + protected override BlockSyntax GetBody(PropertyDeclarationSyntax declaration) => GetBodyFromSingleGetAccessor(declaration.AccessorList); - public override ArrowExpressionClauseSyntax GetExpressionBody(PropertyDeclarationSyntax declaration) + protected override ArrowExpressionClauseSyntax GetExpressionBody(PropertyDeclarationSyntax declaration) => declaration.ExpressionBody; protected override SyntaxToken GetSemicolonToken(PropertyDeclarationSyntax declaration) diff --git a/src/Features/Core/Portable/CodeStyle/AbstractCodeStyleDiagnosticAnalyzer.cs b/src/Features/Core/Portable/CodeStyle/AbstractCodeStyleDiagnosticAnalyzer.cs index 4caa80dfa3283..c0e79ff38211a 100644 --- a/src/Features/Core/Portable/CodeStyle/AbstractCodeStyleDiagnosticAnalyzer.cs +++ b/src/Features/Core/Portable/CodeStyle/AbstractCodeStyleDiagnosticAnalyzer.cs @@ -66,7 +66,7 @@ protected AbstractCodeStyleDiagnosticAnalyzer( HiddenDescriptor, UnnecessaryWithoutSuggestionDescriptor, UnnecessaryWithSuggestionDescriptor); } - public sealed override ImmutableArray SupportedDiagnostics { get; } + public override ImmutableArray SupportedDiagnostics { get; } protected DiagnosticDescriptor GetDescriptorWithSeverity(DiagnosticSeverity severity) { @@ -86,7 +86,7 @@ protected DiagnosticDescriptor CreateDescriptorWithSeverity(DiagnosticSeverity s protected DiagnosticDescriptor CreateDescriptorWithTitle(LocalizableString title, DiagnosticSeverity severity, params string[] customTags) => CreateDescriptorWithId(DescriptorId, title, title, severity, customTags); - private DiagnosticDescriptor CreateDescriptorWithId(string id, LocalizableString title, LocalizableString message, DiagnosticSeverity severity, params string[] customTags) + protected DiagnosticDescriptor CreateDescriptorWithId(string id, LocalizableString title, LocalizableString message, DiagnosticSeverity severity, params string[] customTags) => new DiagnosticDescriptor( id, title, message, DiagnosticCategory.Style, From 1192f0f38509f939fbde28ecf8c9dda677488ca1 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:08:49 -0700 Subject: [PATCH 074/214] Remove dead files. --- .../CSharp/Portable/CSharpFeatures.csproj | 21 ------------------- ...ssionBodyForAccessorsDiagnosticAnalyzer.cs | 18 ---------------- ...onBodyForConstructorsDiagnosticAnalyzer.cs | 18 ---------------- ...orConversionOperatorsDiagnosticAnalyzer.cs | 18 ---------------- ...essionBodyForIndexersDiagnosticAnalyzer.cs | 18 ---------------- ...ressionBodyForMethodsDiagnosticAnalyzer.cs | 18 ---------------- ...ssionBodyForOperatorsDiagnosticAnalyzer.cs | 18 ---------------- ...sionBodyForPropertiesDiagnosticAnalyzer.cs | 18 ---------------- ...pressionBodyForAccessorsCodeFixProvider.cs | 18 ---------------- ...ssionBodyForConstructorsCodeFixProvider.cs | 18 ---------------- ...dyForConversionOperatorsCodeFixProvider.cs | 18 ---------------- ...xpressionBodyForIndexersCodeFixProvider.cs | 18 ---------------- ...ExpressionBodyForMethodsCodeFixProvider.cs | 18 ---------------- ...pressionBodyForOperatorsCodeFixProvider.cs | 18 ---------------- ...ressionBodyForPropertiesCodeFixProvider.cs | 18 ---------------- ...BodyForAccessorsCodeRefactoringProvider.cs | 17 --------------- ...yForConstructorsCodeRefactoringProvider.cs | 17 --------------- ...versionOperatorsCodeRefactoringProvider.cs | 17 --------------- ...nBodyForIndexersCodeRefactoringProvider.cs | 17 --------------- ...onBodyForMethodsCodeRefactoringProvider.cs | 17 --------------- ...BodyForOperatorsCodeRefactoringProvider.cs | 17 --------------- ...odyForPropertiesCodeRefactoringProvider.cs | 17 --------------- 22 files changed, 392 deletions(-) delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs delete mode 100644 src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index beb01101a74cc..ff911c14ce2d3 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -98,32 +98,13 @@ - - - - - - - - - - - - - - - - - - - @@ -419,8 +400,6 @@ - - diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs deleted file mode 100644 index bf900fdb22a21..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForAccessorsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForAccessorsDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForAccessorsDiagnosticAnalyzer() -// : base(UseExpressionBodyForAccessorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs deleted file mode 100644 index a25d297d33f0f..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConstructorsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForConstructorsDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForConstructorsDiagnosticAnalyzer() -// : base(UseExpressionBodyForConstructorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs deleted file mode 100644 index baaf8d5cc119a..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForConversionOperatorsDiagnosticAnalyzer() -// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs deleted file mode 100644 index 8334feedb0ced..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForIndexersDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForIndexersDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForIndexersDiagnosticAnalyzer() -// : base(UseExpressionBodyForIndexersHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs deleted file mode 100644 index b47832a8d8556..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForMethodsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForMethodsDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForMethodsDiagnosticAnalyzer() -// : base(UseExpressionBodyForMethodsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs deleted file mode 100644 index cd81b7cd95d35..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForOperatorsDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForOperatorsDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForOperatorsDiagnosticAnalyzer() -// : base(UseExpressionBodyForOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs deleted file mode 100644 index ce1e300da3b45..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/UseExpressionBodyForPropertiesDiagnosticAnalyzer.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [DiagnosticAnalyzer(LanguageNames.CSharp)] -// internal class UseExpressionBodyForPropertiesDiagnosticAnalyzer : -// AbstractUseExpressionBodyDiagnosticAnalyzer -// { -// public UseExpressionBodyForPropertiesDiagnosticAnalyzer() -// : base(UseExpressionBodyForPropertiesHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs deleted file mode 100644 index 6d25637b09805..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForAccessorsCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForAccessorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForAccessorsCodeFixProvider() -// : base(UseExpressionBodyForAccessorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs deleted file mode 100644 index 75eb89fc8e3fc..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConstructorsCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForConstructorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForConstructorsCodeFixProvider() -// : base(UseExpressionBodyForConstructorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs deleted file mode 100644 index 39dbb32c56497..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForConversionOperatorsCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForConversionOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForConversionOperatorsCodeFixProvider() -// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs deleted file mode 100644 index ab7a90aa39cd1..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForIndexersCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForIndexersCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForIndexersCodeFixProvider() -// : base(UseExpressionBodyForIndexersHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs deleted file mode 100644 index 144f12eae6bd2..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForMethodsCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForMethodsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForMethodsCodeFixProvider() -// : base(UseExpressionBodyForMethodsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs deleted file mode 100644 index 5e749f08ffa56..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForOperatorsCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForOperatorsCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForOperatorsCodeFixProvider() -// : base(UseExpressionBodyForOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs deleted file mode 100644 index 6a5fb9cbef00d..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/UseExpressionBodyForPropertiesCodeFixProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeFixes; -//using Microsoft.CodeAnalysis.CSharp.Syntax; -//using Microsoft.CodeAnalysis.Diagnostics; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeFixProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForPropertiesCodeFixProvider : AbstractUseExpressionBodyCodeFixProvider -// { -// public UseExpressionBodyForPropertiesCodeFixProvider() -// : base(UseExpressionBodyForPropertiesHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs deleted file mode 100644 index 09c62c6115447..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForAccessorsCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForAccessorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForAccessorsCodeRefactoringProvider() -// : base(UseExpressionBodyForAccessorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs deleted file mode 100644 index 849814e1576b3..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConstructorsCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForConstructorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForConstructorsCodeRefactoringProvider() -// : base(UseExpressionBodyForConstructorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs deleted file mode 100644 index f9db715e8fa2f..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForConversionOperatorsCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForConversionOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForConversionOperatorsCodeRefactoringProvider() -// : base(UseExpressionBodyForConversionOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs deleted file mode 100644 index 3e7ba411db9ed..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForIndexersCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForIndexersCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForIndexersCodeRefactoringProvider() -// : base(UseExpressionBodyForIndexersHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs deleted file mode 100644 index 78f633838e03f..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForMethodsCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForMethodsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForMethodsCodeRefactoringProvider() -// : base(UseExpressionBodyForMethodsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs deleted file mode 100644 index 45fbcaf0062a3..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForOperatorsCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForOperatorsCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForOperatorsCodeRefactoringProvider() -// : base(UseExpressionBodyForOperatorsHelper.Instance) -// { -// } -// } -//} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs deleted file mode 100644 index 9bd3f36e3d0ee..0000000000000 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/UseExpressionBodyForPropertiesCodeRefactoringProvider.cs +++ /dev/null @@ -1,17 +0,0 @@ -//// 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.Composition; -//using Microsoft.CodeAnalysis.CodeRefactorings; -//using Microsoft.CodeAnalysis.CSharp.Syntax; - -//namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody -//{ -// [ExportCodeRefactoringProvider(LanguageNames.CSharp), Shared] -// internal class UseExpressionBodyForPropertiesCodeRefactoringProvider : AbstractUseExpressionBodyCodeRefactoringProvider -// { -// public UseExpressionBodyForPropertiesCodeRefactoringProvider() -// : base(UseExpressionBodyForPropertiesHelper.Instance) -// { -// } -// } -//} \ No newline at end of file From 991708115e318cc4eddc3ade89b655a45597df55 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:11:04 -0700 Subject: [PATCH 075/214] Move files. --- .../CSharp/Portable/CSharpFeatures.csproj | 9 ++--- .../UseExpressionBodyForAccessorsHelper.cs | 2 +- .../UseExpressionBodyForConstructorsHelper.cs | 2 +- ...ressionBodyForConversionOperatorsHelper.cs | 2 +- .../UseExpressionBodyForIndexersHelper.cs | 2 +- .../UseExpressionBodyForMethodsHelper.cs | 2 +- .../UseExpressionBodyForOperatorsHelper.cs | 2 +- .../UseExpressionBodyForPropertiesHelper.cs | 2 +- .../Helpers/UseExpressionBodyHelper.cs | 35 +++++++++++++++++++ ...Helper.cs => UseExpressionBodyHelper`1.cs} | 30 ++-------------- ...cs => UseExpressionBodyCodeFixProvider.cs} | 3 +- ...eExpressionBodyCodeRefactoringProvider.cs} | 0 ...=> UseExpressionBodyDiagnosticAnalyzer.cs} | 0 13 files changed, 50 insertions(+), 41 deletions(-) create mode 100644 src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs rename src/Features/CSharp/Portable/UseExpressionBody/Helpers/{AbstractUseExpressionBodyHelper.cs => UseExpressionBodyHelper`1.cs} (85%) rename src/Features/CSharp/Portable/UseExpressionBody/{CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs => UseExpressionBodyCodeFixProvider.cs} (97%) rename src/Features/CSharp/Portable/UseExpressionBody/{CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs => UseExpressionBodyCodeRefactoringProvider.cs} (100%) rename src/Features/CSharp/Portable/UseExpressionBody/{Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs => UseExpressionBodyDiagnosticAnalyzer.cs} (100%) diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index ff911c14ce2d3..a15b756b7d3c3 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -96,8 +96,9 @@ - - + + + @@ -400,8 +401,8 @@ - - + + diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index e797064836315..57a6d7b6602a1 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForAccessorsHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForAccessorsHelper Instance = new UseExpressionBodyForAccessorsHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs index 306183b0c79e9..dbbea2f234c9e 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConstructorsHelper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForConstructorsHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForConstructorsHelper Instance = new UseExpressionBodyForConstructorsHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs index 56eca18c54d91..9c485bc20365b 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForConversionOperatorsHelper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForConversionOperatorsHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForConversionOperatorsHelper Instance = new UseExpressionBodyForConversionOperatorsHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs index 9c86b3ddfd1aa..8150a2ef945ed 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForIndexersHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForIndexersHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForIndexersHelper Instance = new UseExpressionBodyForIndexersHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs index 162252c6ea1bf..57df442633c55 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForMethodsHelper.cs @@ -9,7 +9,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForMethodsHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForMethodsHelper Instance = new UseExpressionBodyForMethodsHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs index 4f4a076d51e1a..0aea6f575551d 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForOperatorsHelper.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForOperatorsHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForOperatorsHelper Instance = new UseExpressionBodyForOperatorsHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 3379d67d51b3b..7709204a32d51 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { internal class UseExpressionBodyForPropertiesHelper : - AbstractUseExpressionBodyHelper + UseExpressionBodyHelper { public static readonly UseExpressionBodyForPropertiesHelper Instance = new UseExpressionBodyForPropertiesHelper(); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs new file mode 100644 index 0000000000000..9957fb1280212 --- /dev/null +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs @@ -0,0 +1,35 @@ +// 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 Microsoft.CodeAnalysis.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Options; + +namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody +{ + internal abstract class UseExpressionBodyHelper + { + public abstract Option> Option { get; } + public abstract LocalizableString UseExpressionBodyTitle { get; } + public abstract LocalizableString UseBlockBodyTitle { get; } + public abstract string DiagnosticId { get; } + public abstract ImmutableArray SyntaxKinds { get; } + + public abstract BlockSyntax GetBody(SyntaxNode declaration); + public abstract ArrowExpressionClauseSyntax GetExpressionBody(SyntaxNode declaration); + + public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); + public abstract bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); + public abstract SyntaxNode Update(SyntaxNode declaration, OptionSet options); + + public static readonly ImmutableArray Helpers = + ImmutableArray.Create( + UseExpressionBodyForConstructorsHelper.Instance, + UseExpressionBodyForConversionOperatorsHelper.Instance, + UseExpressionBodyForIndexersHelper.Instance, + UseExpressionBodyForMethodsHelper.Instance, + UseExpressionBodyForOperatorsHelper.Instance, + UseExpressionBodyForPropertiesHelper.Instance, + UseExpressionBodyForAccessorsHelper.Instance); + } +} \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs similarity index 85% rename from src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs rename to src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index 30548e6b74365..878ec5aad32bd 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/AbstractUseExpressionBodyHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -13,38 +13,12 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { - internal abstract class UseExpressionBodyHelper - { - public abstract Option> Option { get; } - public abstract LocalizableString UseExpressionBodyTitle { get; } - public abstract LocalizableString UseBlockBodyTitle { get; } - public abstract string DiagnosticId { get; } - public abstract ImmutableArray SyntaxKinds { get; } - - public abstract BlockSyntax GetBody(SyntaxNode declaration); - public abstract ArrowExpressionClauseSyntax GetExpressionBody(SyntaxNode declaration); - - public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); - public abstract bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); - public abstract SyntaxNode Update(SyntaxNode declaration, OptionSet options); - - public static readonly ImmutableArray Helpers = - ImmutableArray.Create( - UseExpressionBodyForConstructorsHelper.Instance, - UseExpressionBodyForConversionOperatorsHelper.Instance, - UseExpressionBodyForIndexersHelper.Instance, - UseExpressionBodyForMethodsHelper.Instance, - UseExpressionBodyForOperatorsHelper.Instance, - UseExpressionBodyForPropertiesHelper.Instance, - UseExpressionBodyForAccessorsHelper.Instance); - } - /// /// Helper class that allows us to share lots of logic between the diagnostic analyzer and the /// code refactoring provider. Those can't share a common base class due to their own inheritance /// requirements with and . /// - internal abstract class AbstractUseExpressionBodyHelper : UseExpressionBodyHelper + internal abstract class UseExpressionBodyHelper : UseExpressionBodyHelper where TDeclaration : SyntaxNode { public override Option> Option { get; } @@ -53,7 +27,7 @@ internal abstract class AbstractUseExpressionBodyHelper : UseExpre public override string DiagnosticId { get; } public override ImmutableArray SyntaxKinds { get; } - protected AbstractUseExpressionBodyHelper( + protected UseExpressionBodyHelper( string diagnosticId, LocalizableString useExpressionBodyTitle, LocalizableString useBlockBodyTitle, diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs similarity index 97% rename from src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index 314a305b94129..bdb65c8d435f4 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/CodeFixProviders/AbstractUseExpressionBodyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -17,8 +17,7 @@ namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { [ExportCodeFixProvider(LanguageNames.CSharp), Shared] - internal partial class UseExpressionBodyCodeFixProvider : - SyntaxEditorBasedCodeFixProvider + internal partial class UseExpressionBodyCodeFixProvider : SyntaxEditorBasedCodeFixProvider { public sealed override ImmutableArray FixableDiagnosticIds { get; } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs similarity index 100% rename from src/Features/CSharp/Portable/UseExpressionBody/CodeRefactoringProviders/AbstractUseExpressionBodyCodeRefactoringProvider.cs rename to src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs similarity index 100% rename from src/Features/CSharp/Portable/UseExpressionBody/Analyzers/AbstractUseExpressionBodyDiagnosticAnalyzer.cs rename to src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs From a950d9a96a33a696f5104dae468711e9adb68616 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:13:16 -0700 Subject: [PATCH 076/214] Remove dead code. --- .../UseExpressionBodyForAccessorsHelper.cs | 53 ------------------- 1 file changed, 53 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 57a6d7b6602a1..72e28905f54eb 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -14,14 +14,6 @@ internal class UseExpressionBodyForAccessorsHelper : { public static readonly UseExpressionBodyForAccessorsHelper Instance = new UseExpressionBodyForAccessorsHelper(); - //private static readonly Func _propertyCanOfferUseExpressionBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseExpressionBody; - //private static readonly Func _propertyCanOfferUseBlockBody = UseExpressionBodyForPropertiesHelper.Instance.CanOfferUseBlockBody; - //private static readonly Func _indexerCanOfferUseExpressionBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseExpressionBody; - //private static readonly Func _indexerCanOfferUseBlockBody = UseExpressionBodyForIndexersHelper.Instance.CanOfferUseBlockBody; - - //private readonly Func _baseCanOfferUseExpressionBody; - //private readonly Func _baseCanOfferUseBlockBody; - private UseExpressionBodyForAccessorsHelper() : base(IDEDiagnosticIds.UseExpressionBodyForAccessorsDiagnosticId, new LocalizableResourceString(nameof(FeaturesResources.Use_expression_body_for_accessors), FeaturesResources.ResourceManager, typeof(FeaturesResources)), @@ -29,8 +21,6 @@ private UseExpressionBodyForAccessorsHelper() CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, ImmutableArray.Create(SyntaxKind.GetAccessorDeclaration, SyntaxKind.SetAccessorDeclaration)) { - //_baseCanOfferUseExpressionBody = base.CanOfferUseExpressionBody; - //_baseCanOfferUseBlockBody = base.CanOfferUseBlockBody; } protected override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) @@ -39,49 +29,6 @@ protected override BlockSyntax GetBody(AccessorDeclarationSyntax declaration) protected override ArrowExpressionClauseSyntax GetExpressionBody(AccessorDeclarationSyntax declaration) => declaration.ExpressionBody; - //private bool CanOffer( - // OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer, - // Func propertyPredicate, - // Func indexerPredicate, - // Func basePredicate) - //{ - // var grandParent = accessor.Parent.Parent; - - // if (grandParent.IsKind(SyntaxKind.PropertyDeclaration)) - // { - // var propertyDeclaration = (PropertyDeclarationSyntax)grandParent; - // if (propertyPredicate(optionSet, propertyDeclaration, forAnalyzer)) - // { - // return false; - // } - // } - // else if (grandParent.IsKind(SyntaxKind.IndexerDeclaration)) - // { - // var indexerDeclaration = (IndexerDeclarationSyntax)grandParent; - // if (indexerPredicate(optionSet, indexerDeclaration, forAnalyzer)) - // { - // return false; - // } - // } - - // return basePredicate(optionSet, accessor, forAnalyzer); - //} - - //public override bool CanOfferUseExpressionBody(OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) - //{ - // return CanOffer( - // optionSet, accessor, forAnalyzer, - // _propertyCanOfferUseExpressionBody, _indexerCanOfferUseExpressionBody, _baseCanOfferUseExpressionBody); - //} - - //public override bool CanOfferUseBlockBody( - // OptionSet optionSet, AccessorDeclarationSyntax accessor, bool forAnalyzer) - //{ - // return CanOffer( - // optionSet, accessor, forAnalyzer, - // _propertyCanOfferUseBlockBody, _indexerCanOfferUseBlockBody, _baseCanOfferUseBlockBody); - //} - protected override SyntaxToken GetSemicolonToken(AccessorDeclarationSyntax declaration) => declaration.SemicolonToken; From a088aaf16b2a973c2214e3d3c5863b922648dbf6 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:14:07 -0700 Subject: [PATCH 077/214] Remove usings. --- .../Helpers/UseExpressionBodyForAccessorsHelper.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs index 72e28905f54eb..e6d8d4e046e17 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForAccessorsHelper.cs @@ -1,11 +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.Immutable; using Microsoft.CodeAnalysis.CSharp.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.CSharp.UseExpressionBody { From 5dbaafdae8f9f90a9c83ac9152d543e238eb1ca9 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 1 May 2017 21:26:54 -0700 Subject: [PATCH 078/214] Fix doc comment. --- .../UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs index a51c3e5b65f0d..5beacb2e211ea 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs @@ -99,7 +99,7 @@ private Diagnostic AnalyzeSyntax( if (helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: true)) { - // They have an expression body. Create a diagnostic to conver it to a block + // They have an expression body. Create a diagnostic to convert it to a block // if they don't want expression bodies for this member. var location = severity == DiagnosticSeverity.Hidden ? declaration.GetLocation() From 64d06fd81bf296f03bc4157a7ffa90c82839b743 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 2 May 2017 10:34:39 -0500 Subject: [PATCH 079/214] Remove unnecessary delegate allocations when calling back to analyzers --- .../DiagnosticAnalyzer/AnalyzerExecutor.cs | 183 ++++++++++++------ ...nalyzerManager.AnalyzerExecutionContext.cs | 17 +- 2 files changed, 129 insertions(+), 71 deletions(-) diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs index ef3838de66839..17e26b8a2636c 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerExecutor.cs @@ -201,9 +201,13 @@ public AnalyzerExecutor WithCancellationToken(CancellationToken cancellationToke /// public void ExecuteInitializeMethod(DiagnosticAnalyzer analyzer, HostSessionStartAnalysisScope sessionScope) { + var context = new AnalyzerAnalysisContext(analyzer, sessionScope, _compilation.IsIOperationFeatureEnabled()); + // The Initialize method should be run asynchronously in case it is not well behaved, e.g. does not terminate. - ExecuteAndCatchIfThrows(analyzer, - () => analyzer.Initialize(new AnalyzerAnalysisContext(analyzer, sessionScope, _compilation.IsIOperationFeatureEnabled()))); + ExecuteAndCatchIfThrows( + analyzer, + data => data.analyzer.Initialize(data.context), + (analyzer: analyzer, context: context)); } /// @@ -217,9 +221,13 @@ public void ExecuteCompilationStartActions(ImmutableArray startAction.Action(new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, - _compilation, _analyzerOptions, _compilationAnalysisValueProviderFactory, _cancellationToken)), + var context = new AnalyzerCompilationStartAnalysisContext(startAction.Analyzer, compilationScope, + _compilation, _analyzerOptions, _compilationAnalysisValueProviderFactory, _cancellationToken); + + ExecuteAndCatchIfThrows( + startAction.Analyzer, + data => data.action(data.context), + (action: startAction.Action, context: context), new AnalysisContextInfo(_compilation)); } } @@ -267,6 +275,7 @@ public bool TryExecuteCompilationActions( private void ExecuteCompilationActionsCore(ImmutableArray compilationActions, DiagnosticAnalyzer analyzer, AnalyzerStateData analyzerStateOpt) { var addDiagnostic = GetAddCompilationDiagnostic(analyzer); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); foreach (var endAction in compilationActions) { @@ -274,10 +283,14 @@ private void ExecuteCompilationActionsCore(ImmutableArray endAction.Action(new CompilationAnalysisContext( - _compilation, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(endAction.Analyzer, d), _compilationAnalysisValueProviderFactory, _cancellationToken)), + var context = new CompilationAnalysisContext( + _compilation, _analyzerOptions, addDiagnostic, + isSupportedDiagnostic, _compilationAnalysisValueProviderFactory, _cancellationToken); + + ExecuteAndCatchIfThrows( + endAction.Analyzer, + data => data.action(data.context), + (action: endAction.Action, context: context), new AnalysisContextInfo(_compilation)); analyzerStateOpt?.ProcessedActions.Add(endAction); @@ -345,6 +358,7 @@ private void ExecuteSymbolActionsCore( var symbol = symbolDeclaredEvent.Symbol; var addDiagnostic = GetAddDiagnostic(symbol, symbolDeclaredEvent.DeclaringSyntaxReferences, analyzer, getTopMostNodeForAnalysis); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); foreach (var symbolAction in symbolActions) { @@ -357,9 +371,13 @@ private void ExecuteSymbolActionsCore( { _cancellationToken.ThrowIfCancellationRequested(); - ExecuteAndCatchIfThrows(symbolAction.Analyzer, - () => action(new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(symbolAction.Analyzer, d), _cancellationToken)), + var context = new SymbolAnalysisContext(symbol, _compilation, _analyzerOptions, addDiagnostic, + isSupportedDiagnostic, _cancellationToken); + + ExecuteAndCatchIfThrows( + symbolAction.Analyzer, + data => data.action(data.context), + (action: action, context: context), new AnalysisContextInfo(_compilation, symbol)); analyzerStateOpt?.ProcessedActions.Add(symbolAction); @@ -423,6 +441,7 @@ private void ExecuteSemanticModelActionsCore( } var addDiagnostic = GetAddDiagnostic(semanticModel.SyntaxTree, analyzer, isSyntaxDiagnostic: false); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); foreach (var semanticModelAction in semanticModelActions) { @@ -430,10 +449,14 @@ private void ExecuteSemanticModelActionsCore( { _cancellationToken.ThrowIfCancellationRequested(); + var context = new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, addDiagnostic, + isSupportedDiagnostic, _cancellationToken); + // Catch Exception from action. - ExecuteAndCatchIfThrows(semanticModelAction.Analyzer, - () => semanticModelAction.Action(new SemanticModelAnalysisContext(semanticModel, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(semanticModelAction.Analyzer, d), _cancellationToken)), + ExecuteAndCatchIfThrows( + semanticModelAction.Analyzer, + data => data.action(data.context), + (action: semanticModelAction.Action, context: context), new AnalysisContextInfo(semanticModel)); analyzerStateOpt?.ProcessedActions.Add(semanticModelAction); @@ -494,6 +517,7 @@ private void ExecuteSyntaxTreeActionsCore( } var addDiagnostic = GetAddDiagnostic(tree, analyzer, isSyntaxDiagnostic: true); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); foreach (var syntaxTreeAction in syntaxTreeActions) { @@ -501,10 +525,13 @@ private void ExecuteSyntaxTreeActionsCore( { _cancellationToken.ThrowIfCancellationRequested(); + var context = new SyntaxTreeAnalysisContext(tree, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _compilation, _cancellationToken); + // Catch Exception from action. - ExecuteAndCatchIfThrows(syntaxTreeAction.Analyzer, - () => syntaxTreeAction.Action(new SyntaxTreeAnalysisContext(tree, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(syntaxTreeAction.Analyzer, d), _compilation, _cancellationToken)), + ExecuteAndCatchIfThrows( + syntaxTreeAction.Analyzer, + data => data.action(data.context), + (action: syntaxTreeAction.Action, context: context), new AnalysisContextInfo(_compilation, tree)); analyzerStateOpt?.ProcessedActions.Add(syntaxTreeAction); @@ -518,6 +545,7 @@ private void ExecuteSyntaxNodeAction( ISymbol containingSymbol, SemanticModel semanticModel, Action addDiagnostic, + Func isSupportedDiagnostic, SyntaxNodeAnalyzerStateData analyzerStateOpt) where TLanguageKindEnum : struct { @@ -526,9 +554,12 @@ private void ExecuteSyntaxNodeAction( if (ShouldExecuteAction(analyzerStateOpt, syntaxNodeAction)) { var syntaxNodeContext = new SyntaxNodeAnalysisContext(node, containingSymbol, semanticModel, _analyzerOptions, addDiagnostic, - d => IsSupportedDiagnostic(syntaxNodeAction.Analyzer, d), _cancellationToken); - ExecuteAndCatchIfThrows(syntaxNodeAction.Analyzer, - () => syntaxNodeAction.Action(syntaxNodeContext), + isSupportedDiagnostic, _cancellationToken); + + ExecuteAndCatchIfThrows( + syntaxNodeAction.Analyzer, + data => data.action(data.context), + (action: syntaxNodeAction.Action, context: syntaxNodeContext), new AnalysisContextInfo(_compilation, node)); analyzerStateOpt?.ProcessedActions.Add(syntaxNodeAction); @@ -541,15 +572,18 @@ private void ExecuteOperationAction( ISymbol containingSymbol, SemanticModel semanticModel, Action addDiagnostic, + Func isSupportedDiagnostic, OperationAnalyzerStateData analyzerStateOpt) { Debug.Assert(analyzerStateOpt == null || analyzerStateOpt.CurrentOperation == operation); if (ShouldExecuteAction(analyzerStateOpt, operationAction)) { - var operationContext = new OperationAnalysisContext(operation, containingSymbol, semanticModel.Compilation, _analyzerOptions, addDiagnostic, d => IsSupportedDiagnostic(operationAction.Analyzer, d), _cancellationToken); - ExecuteAndCatchIfThrows(operationAction.Analyzer, - () => operationAction.Action(operationContext), + var operationContext = new OperationAnalysisContext(operation, containingSymbol, semanticModel.Compilation, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken); + ExecuteAndCatchIfThrows( + operationAction.Analyzer, + data => data.action(data.context), + (action: operationAction.Action, context: operationContext), new AnalysisContextInfo(_compilation, operation)); analyzerStateOpt?.ProcessedActions.Add(operationAction); @@ -701,6 +735,7 @@ private void ExecuteBlockActionsCore isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); try { @@ -713,17 +748,21 @@ private void ExecuteBlockActionsCore; + var codeBlockScope = new HostCodeBlockStartAnalysisScope(); + var blockStartContext = new AnalyzerCodeBlockStartAnalysisContext(startAction.Analyzer, + codeBlockScope, declaredNode, declaredSymbol, semanticModel, _analyzerOptions, _cancellationToken); + // Catch Exception from the start action. - ExecuteAndCatchIfThrows(startAction.Analyzer, () => - { - var codeBlockScope = new HostCodeBlockStartAnalysisScope(); - var blockStartContext = new AnalyzerCodeBlockStartAnalysisContext(startAction.Analyzer, - codeBlockScope, declaredNode, declaredSymbol, semanticModel, _analyzerOptions, _cancellationToken); - codeBlockStartAction.Action(blockStartContext); - codeBlockEndActions.AddAll(codeBlockScope.CodeBlockEndActions); - syntaxNodeActions.AddRange(codeBlockScope.SyntaxNodeActions); - }, - new AnalysisContextInfo(_compilation, declaredSymbol, declaredNode)); + ExecuteAndCatchIfThrows( + startAction.Analyzer, + data => + { + data.action(data.context); + data.blockEndActions.AddAll(data.scope.CodeBlockEndActions); + data.syntaxNodeActions.AddRange(data.scope.SyntaxNodeActions); + }, + (action: codeBlockStartAction.Action, context: blockStartContext, scope: codeBlockScope, blockEndActions: codeBlockEndActions, syntaxNodeActions: syntaxNodeActions), + new AnalysisContextInfo(_compilation, declaredSymbol, declaredNode)); } else { @@ -731,17 +770,21 @@ private void ExecuteBlockActionsCore; + var operationBlockScope = new HostOperationBlockStartAnalysisScope(); + var operationStartContext = new AnalyzerOperationBlockStartAnalysisContext(startAction.Analyzer, + operationBlockScope, operationBlocks, declaredSymbol, semanticModel.Compilation, _analyzerOptions, _cancellationToken); + // Catch Exception from the start action. - ExecuteAndCatchIfThrows(startAction.Analyzer, () => - { - var operationBlockScope = new HostOperationBlockStartAnalysisScope(); - var operationStartContext = new AnalyzerOperationBlockStartAnalysisContext(startAction.Analyzer, - operationBlockScope, operationBlocks, declaredSymbol, semanticModel.Compilation, _analyzerOptions, _cancellationToken); - operationBlockStartAction.Action(operationStartContext); - operationBlockEndActions.AddAll(operationBlockScope.OperationBlockEndActions); - operationActions.AddRange(operationBlockScope.OperationActions); - }, - new AnalysisContextInfo(_compilation, declaredSymbol)); + ExecuteAndCatchIfThrows( + startAction.Analyzer, + data => + { + data.action(data.context); + data.blockEndActions.AddAll(data.scope.OperationBlockEndActions); + data.operationActions.AddRange(data.scope.OperationActions); + }, + (action: operationBlockStartAction.Action, context: operationStartContext, scope: operationBlockScope, blockEndActions: operationBlockEndActions, operationActions: operationActions), + new AnalysisContextInfo(_compilation, declaredSymbol)); } } @@ -765,20 +808,20 @@ private void ExecuteBlockActionsCore)getNodesToAnalyze(executableBlocks); - ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); + ExecuteSyntaxNodeActions(syntaxNodesToAnalyze, executableNodeActionsByKind, analyzer, declaredSymbol, semanticModel, getKind, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as SyntaxNodeAnalyzerStateData); } else if (operationActions != null) { var operationActionsByKind = GetOperationActionsByKind(operationActions); var operationsToAnalyze = (IEnumerable)getNodesToAnalyze(executableBlocks); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, addDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); + ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, declaredSymbol, semanticModel, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt?.ExecutableNodesAnalysisState as OperationAnalyzerStateData); } } executableNodeActions.Free(); - ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt); - ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, analyzerStateOpt); + ExecuteBlockActions(blockActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); + ExecuteBlockActions(blockEndActions, declaredNode, declaredSymbol, semanticModel, operationBlocks, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } private void ExecuteBlockActions( @@ -788,6 +831,7 @@ private void ExecuteBlockActions( SemanticModel semanticModel, ImmutableArray operationBlocks, Action addDiagnostic, + Func isSupportedDiagnostic, AnalysisState.BlockAnalyzerStateData analyzerStateOpt) where TBlockAction : AnalyzerAction where TNodeStateData : AnalyzerStateData, new() @@ -797,12 +841,14 @@ private void ExecuteBlockActions( if (ShouldExecuteAction(analyzerStateOpt, blockAction)) { var codeBlockAction = blockAction as CodeBlockAnalyzerAction; - Func isSupportedDiagnostic = d => IsSupportedDiagnostic(blockAction.Analyzer, d); if (codeBlockAction != null) { + var context = new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken); + ExecuteAndCatchIfThrows( codeBlockAction.Analyzer, - () => codeBlockAction.Action(new CodeBlockAnalysisContext(declaredNode, declaredSymbol, semanticModel, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken)), + data => data.action(data.context), + (action: codeBlockAction.Action, context: context), new AnalysisContextInfo(_compilation, declaredSymbol, declaredNode)); } else @@ -810,9 +856,12 @@ private void ExecuteBlockActions( var operationBlockAction = blockAction as OperationBlockAnalyzerAction; if (operationBlockAction != null) { + var context = new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken); + ExecuteAndCatchIfThrows( operationBlockAction.Analyzer, - () => operationBlockAction.Action(new OperationBlockAnalysisContext(operationBlocks, declaredSymbol, semanticModel.Compilation, _analyzerOptions, addDiagnostic, isSupportedDiagnostic, _cancellationToken)), + data => data.action(data.context), + (action: operationBlockAction.Action, context: context), new AnalysisContextInfo(_compilation, declaredSymbol)); } } @@ -909,7 +958,8 @@ private void ExecuteSyntaxNodeActionsCore( } var addDiagnostic = GetAddDiagnostic(model.SyntaxTree, filterSpan, analyzer, isSyntaxDiagnostic: false); - ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, containingSymbol, model, getKind, addDiagnostic, analyzerStateOpt); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); + ExecuteSyntaxNodeActions(nodesToAnalyze, nodeActionsByKind, analyzer, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } private void ExecuteSyntaxNodeActions( @@ -920,6 +970,7 @@ private void ExecuteSyntaxNodeActions( SemanticModel model, Func getKind, Action addDiagnostic, + Func isSupportedDiagnostic, SyntaxNodeAnalyzerStateData analyzerStateOpt) where TLanguageKindEnum : struct { @@ -929,7 +980,7 @@ private void ExecuteSyntaxNodeActions( SyntaxNode partiallyProcessedNode = analyzerStateOpt?.CurrentNode; if (partiallyProcessedNode != null) { - ExecuteSyntaxNodeActions(partiallyProcessedNode, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, analyzerStateOpt); + ExecuteSyntaxNodeActions(partiallyProcessedNode, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } foreach (var child in nodesToAnalyze) @@ -938,7 +989,7 @@ private void ExecuteSyntaxNodeActions( { SetCurrentNode(analyzerStateOpt, child); - ExecuteSyntaxNodeActions(child, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, analyzerStateOpt); + ExecuteSyntaxNodeActions(child, nodeActionsByKind, containingSymbol, model, getKind, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } } } @@ -950,6 +1001,7 @@ private void ExecuteSyntaxNodeActions( SemanticModel model, Func getKind, Action addDiagnostic, + Func isSupportedDiagnostic, SyntaxNodeAnalyzerStateData analyzerStateOpt) where TLanguageKindEnum : struct { @@ -958,7 +1010,7 @@ private void ExecuteSyntaxNodeActions( { foreach (var action in actionsForKind) { - ExecuteSyntaxNodeAction(action, node, containingSymbol, model, addDiagnostic, analyzerStateOpt); + ExecuteSyntaxNodeAction(action, node, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } } @@ -1044,7 +1096,8 @@ private void ExecuteOperationActionsCore( } var addDiagnostic = GetAddDiagnostic(model.SyntaxTree, filterSpan, analyzer, isSyntaxDiagnostic: false); - ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, containingSymbol, model, addDiagnostic, analyzerStateOpt); + Func isSupportedDiagnostic = d => IsSupportedDiagnostic(analyzer, d); + ExecuteOperationActions(operationsToAnalyze, operationActionsByKind, analyzer, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } private void ExecuteOperationActions( @@ -1054,6 +1107,7 @@ private void ExecuteOperationActions( ISymbol containingSymbol, SemanticModel model, Action addDiagnostic, + Func isSupportedDiagnostic, OperationAnalyzerStateData analyzerStateOpt) { Debug.Assert(operationActionsByKind != null); @@ -1062,7 +1116,7 @@ private void ExecuteOperationActions( IOperation partiallyProcessedNode = analyzerStateOpt?.CurrentOperation; if (partiallyProcessedNode != null) { - ExecuteOperationActions(partiallyProcessedNode, operationActionsByKind, containingSymbol, model, addDiagnostic, analyzerStateOpt); + ExecuteOperationActions(partiallyProcessedNode, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } foreach (var child in operationsToAnalyze) @@ -1071,7 +1125,7 @@ private void ExecuteOperationActions( { SetCurrentOperation(analyzerStateOpt, child); - ExecuteOperationActions(child, operationActionsByKind, containingSymbol, model, addDiagnostic, analyzerStateOpt); + ExecuteOperationActions(child, operationActionsByKind, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } } } @@ -1082,6 +1136,7 @@ private void ExecuteOperationActions( ISymbol containingSymbol, SemanticModel model, Action addDiagnostic, + Func isSupportedDiagnostic, OperationAnalyzerStateData analyzerStateOpt) { ImmutableArray actionsForKind; @@ -1089,7 +1144,7 @@ private void ExecuteOperationActions( { foreach (var action in actionsForKind) { - ExecuteOperationAction(action, operation, containingSymbol, model, addDiagnostic, analyzerStateOpt); + ExecuteOperationAction(action, operation, containingSymbol, model, addDiagnostic, isSupportedDiagnostic, analyzerStateOpt); } } @@ -1115,23 +1170,23 @@ internal static bool CanHaveExecutableCodeBlock(ISymbol symbol) } } - internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyze, AnalysisContextInfo? info = null) + internal void ExecuteAndCatchIfThrows(DiagnosticAnalyzer analyzer, Action analyze, TArg argument, AnalysisContextInfo? info = null) { object gate = _getAnalyzerGateOpt?.Invoke(analyzer); if (gate != null) { lock (gate) { - ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, info); + ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, argument, info); } } else { - ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, info); + ExecuteAndCatchIfThrows_NoLock(analyzer, analyze, argument, info); } } - private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action analyze, AnalysisContextInfo? info) + private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action analyze, TArg argument, AnalysisContextInfo? info) { try { @@ -1143,7 +1198,7 @@ private void ExecuteAndCatchIfThrows_NoLock(DiagnosticAnalyzer analyzer, Action timer = Stopwatch.StartNew(); } - analyze(); + analyze(argument); if (timer != null) { diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs index e190a47a203c6..6826fb443eece 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/AnalyzerManager.AnalyzerExecutionContext.cs @@ -151,14 +151,17 @@ private static ImmutableArray ComputeDescriptors( var supportedDiagnostics = ImmutableArray.Empty; // Catch Exception from analyzer.SupportedDiagnostics - analyzerExecutor.ExecuteAndCatchIfThrows(analyzer, () => - { - var supportedDiagnosticsLocal = analyzer.SupportedDiagnostics; - if (!supportedDiagnosticsLocal.IsDefaultOrEmpty) + analyzerExecutor.ExecuteAndCatchIfThrows( + analyzer, + _ => { - supportedDiagnostics = supportedDiagnosticsLocal; - } - }); + var supportedDiagnosticsLocal = analyzer.SupportedDiagnostics; + if (!supportedDiagnosticsLocal.IsDefaultOrEmpty) + { + supportedDiagnostics = supportedDiagnosticsLocal; + } + }, + argument: default(object)); // Force evaluate and report exception diagnostics from LocalizableString.ToString(). Action onAnalyzerException = analyzerExecutor.OnAnalyzerException; From 99cb318cb23fadf8466fea3902d29e95a50bd726 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 10:54:33 -0700 Subject: [PATCH 080/214] Support converting an expression-bodied accessor to an expression bodied property. --- .../UseExpressionBodyForPropertiesHelper.cs | 37 ++++++++++++++ .../Helpers/UseExpressionBodyHelper.cs | 5 +- .../Helpers/UseExpressionBodyHelper`1.cs | 48 ++++++++++++------- .../UseExpressionBodyCodeFixProvider.cs | 5 +- ...seExpressionBodyCodeRefactoringProvider.cs | 12 +++-- .../UseExpressionBodyDiagnosticAnalyzer.cs | 5 +- .../Extensions/BlockSyntaxExtensions.cs | 2 +- 7 files changed, 88 insertions(+), 26 deletions(-) diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 7709204a32d51..26954810c2cb7 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -2,7 +2,9 @@ using System; using System.Collections.Immutable; +using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.CodeStyle; +using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; @@ -58,5 +60,40 @@ protected override PropertyDeclarationSyntax WithGenerateBody( } protected override bool CreateReturnStatementForExpression(PropertyDeclarationSyntax declaration) => true; + + protected override bool TryConvertToExpressionBody( + PropertyDeclarationSyntax declaration, ParseOptions options, + ExpressionBodyPreference conversionPreference, + out ArrowExpressionClauseSyntax arrowExpression, + out SyntaxToken semicolonToken) + { + if (base.TryConvertToExpressionBody(declaration, options, conversionPreference, out arrowExpression, out semicolonToken)) + { + return true; + } + + var getAccessor = GetSingleGetAccessor(declaration.AccessorList); + if (getAccessor.ExpressionBody != null && + BlockSyntaxExtensions.MatchesPreference(getAccessor.ExpressionBody.Expression, conversionPreference)) + { + arrowExpression = SyntaxFactory.ArrowExpressionClause(getAccessor.ExpressionBody.Expression); + semicolonToken = getAccessor.SemicolonToken; + return true; + } + + return false; + } + + protected override Location GetDiagnosticLocation(PropertyDeclarationSyntax declaration) + { + var body = GetBody(declaration); + if (body != null) + { + return base.GetDiagnosticLocation(declaration); + } + + var getAccessor = GetSingleGetAccessor(declaration.AccessorList); + return getAccessor.ExpressionBody.GetLocation(); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs index 9957fb1280212..db0e707d6d937 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper.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; using System.Collections.Immutable; using Microsoft.CodeAnalysis.CodeStyle; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -20,7 +21,9 @@ internal abstract class UseExpressionBodyHelper public abstract bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); public abstract bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer); - public abstract SyntaxNode Update(SyntaxNode declaration, OptionSet options); + public abstract SyntaxNode Update(SyntaxNode declaration, OptionSet options, bool useExpressionBody); + + public abstract Location GetDiagnosticLocation(SyntaxNode declaration); public static readonly ImmutableArray Helpers = ImmutableArray.Create( diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index 878ec5aad32bd..b5d7004634ed7 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -41,19 +41,23 @@ protected UseExpressionBodyHelper( SyntaxKinds = syntaxKinds; } - protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList) + protected static AccessorDeclarationSyntax GetSingleGetAccessor(AccessorListSyntax accessorList) { if (accessorList != null && accessorList.Accessors.Count == 1 && - accessorList.Accessors[0].AttributeLists.Count == 0 && + accessorList.Accessors[0].AttributeLists.Count == 0 && accessorList.Accessors[0].IsKind(SyntaxKind.GetAccessorDeclaration)) { - return accessorList.Accessors[0].Body; + return accessorList.Accessors[0]; } return null; } + + protected static BlockSyntax GetBodyFromSingleGetAccessor(AccessorListSyntax accessorList) + => GetSingleGetAccessor(accessorList)?.Body; + public override BlockSyntax GetBody(SyntaxNode declaration) => GetBody((TDeclaration)declaration); @@ -66,8 +70,14 @@ public override bool CanOfferUseExpressionBody(OptionSet optionSet, SyntaxNode d public override bool CanOfferUseBlockBody(OptionSet optionSet, SyntaxNode declaration, bool forAnalyzer) => CanOfferUseBlockBody(optionSet, (TDeclaration)declaration, forAnalyzer); - public override SyntaxNode Update(SyntaxNode declaration, OptionSet options) - => Update((TDeclaration)declaration, options); + public override SyntaxNode Update(SyntaxNode declaration, OptionSet options, bool useExpressionBody) + => Update((TDeclaration)declaration, options, useExpressionBody); + + public override Location GetDiagnosticLocation(SyntaxNode declaration) + => GetDiagnosticLocation((TDeclaration)declaration); + + protected virtual Location GetDiagnosticLocation(TDeclaration declaration) + => this.GetBody(declaration).Statements[0].GetLocation(); public virtual bool CanOfferUseExpressionBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) @@ -86,21 +96,28 @@ public virtual bool CanOfferUseExpressionBody( // have into one. var options = declaration.SyntaxTree.Options; - var body = this.GetBody(declaration); - var conversionPreference = forAnalyzer ? preference : ExpressionBodyPreference.WhenPossible; - if (body.TryConvertToExpressionBody(options, conversionPreference, - out var expressionWhenOnSingleLine, out var semicolonWhenOnSingleLine)) - { - return true; - } + return TryConvertToExpressionBody(declaration, options, conversionPreference, + out var expressionWhenOnSingleLine, out var semicolonWhenOnSingleLine); } } return false; } + protected virtual bool TryConvertToExpressionBody( + TDeclaration declaration, + ParseOptions options, ExpressionBodyPreference conversionPreference, + out ArrowExpressionClauseSyntax expressionWhenOnSingleLine, + out SyntaxToken semicolonWhenOnSingleLine) + { + var body = this.GetBody(declaration); + + return body.TryConvertToExpressionBody(options, conversionPreference, + out expressionWhenOnSingleLine, out semicolonWhenOnSingleLine); + } + public virtual bool CanOfferUseBlockBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { @@ -118,12 +135,11 @@ public virtual bool CanOfferUseBlockBody( return false; } - public TDeclaration Update(TDeclaration declaration, OptionSet options) + public TDeclaration Update(TDeclaration declaration, OptionSet options, bool useExpressionBody) { - var preferExpressionBody = GetBody(declaration) != null; - if (preferExpressionBody) + if (useExpressionBody) { - GetBody(declaration).TryConvertToExpressionBody(declaration.SyntaxTree.Options, + TryConvertToExpressionBody(declaration, declaration.SyntaxTree.Options, ExpressionBodyPreference.WhenPossible, out var expressionBody, out var semicolonToken); var trailingTrivia = semicolonToken.TrailingTrivia diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs index bdb65c8d435f4..61f5f3a4d6d8b 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeFixProvider.cs @@ -59,14 +59,15 @@ protected override async Task FixAllAsync( } private void AddEdits( - SyntaxEditor editor, Diagnostic diagnostic, + SyntaxEditor editor, Diagnostic diagnostic, OptionSet options, CancellationToken cancellationToken) { var declarationLocation = diagnostic.AdditionalLocations[0]; var helper = _helpers.Single(h => h.DiagnosticId == diagnostic.Id); var declaration = declarationLocation.FindNode(cancellationToken); + var useExpressionBody = diagnostic.Properties.ContainsKey(nameof(UseExpressionBody)); - var updatedDeclaration = helper.Update(declaration, options) + var updatedDeclaration = helper.Update(declaration, options, useExpressionBody) .WithAdditionalAnnotations(Formatter.Annotation); editor.ReplaceNode(declaration, updatedDeclaration); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs index f1506178e53ed..c2f4d94fb758b 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyCodeRefactoringProvider.cs @@ -82,7 +82,9 @@ private bool TryComputeRefactoring( { context.RegisterRefactoring(new MyCodeAction( helper.UseExpressionBodyTitle.ToString(), - c => UpdateDocumentAsync(document, root, declaration, optionSet, helper, c))); + c => UpdateDocumentAsync( + document, root, declaration, optionSet, helper, + useExpressionBody: true, cancellationToken: c))); succeeded = true; } @@ -90,7 +92,9 @@ private bool TryComputeRefactoring( { context.RegisterRefactoring(new MyCodeAction( helper.UseBlockBodyTitle.ToString(), - c => UpdateDocumentAsync(document, root, declaration, optionSet, helper, c))); + c => UpdateDocumentAsync( + document, root, declaration, optionSet, helper, + useExpressionBody: false, cancellationToken: c))); succeeded = true; } @@ -112,10 +116,10 @@ private SyntaxNode GetDeclaration(SyntaxNode node, UseExpressionBodyHelper helpe private Task UpdateDocumentAsync( Document document, SyntaxNode root, SyntaxNode declaration, - OptionSet options, UseExpressionBodyHelper helper, + OptionSet options, UseExpressionBodyHelper helper, bool useExpressionBody, CancellationToken cancellationToken) { - var updatedDeclaration = helper.Update(declaration, options) + var updatedDeclaration = helper.Update(declaration, options, useExpressionBody) .WithAdditionalAnnotations(Formatter.Annotation); var newRoot = root.ReplaceNode(declaration, updatedDeclaration); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs index 5beacb2e211ea..e5c854ebb52a1 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/UseExpressionBodyDiagnosticAnalyzer.cs @@ -89,12 +89,13 @@ private Diagnostic AnalyzeSyntax( { var location = severity == DiagnosticSeverity.Hidden ? declaration.GetLocation() - : helper.GetBody(declaration).Statements[0].GetLocation(); + : helper.GetDiagnosticLocation(declaration); var additionalLocations = ImmutableArray.Create(declaration.GetLocation()); + var properties = ImmutableDictionary.Empty.Add(nameof(UseExpressionBody), ""); return Diagnostic.Create( CreateDescriptorWithId(helper.DiagnosticId, helper.UseExpressionBodyTitle, helper.UseExpressionBodyTitle, severity, GetCustomTags(severity)), - location, additionalLocations: additionalLocations); + location, additionalLocations: additionalLocations, properties: properties); } if (helper.CanOfferUseBlockBody(optionSet, declaration, forAnalyzer: true)) diff --git a/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs index 8ae96f694fb12..fa2d3b936d989 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs @@ -45,7 +45,7 @@ public static bool TryConvertToExpressionBody( return false; } - private static bool MatchesPreference( + public static bool MatchesPreference( ExpressionSyntax expression, ExpressionBodyPreference preference) { if (preference == ExpressionBodyPreference.WhenPossible) From d0b2b03a71a0e713d6adda8bc768073ce512d082 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:02:19 -0700 Subject: [PATCH 081/214] Update tests. --- ...ExpressionBodyForAccessorsAnalyzerTests.cs | 19 ++++++++++------- ...ressionBodyForAccessorsRefactoringTests.cs | 21 ++++++------------- .../UseExpressionBodyForPropertiesHelper.cs | 2 +- .../Helpers/UseExpressionBodyHelper`1.cs | 2 +- 4 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs index 01f67442394cf..e9b450dbbf328 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForAccessorsAnalyzerTests.cs @@ -32,8 +32,11 @@ internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProvider SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement), SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement)); - private IDictionary UseBlockBody => - Option(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSuggestionEnforcement); + private IDictionary UseBlockBodyIncludingPropertiesAndIndexers => + OptionsSet( + SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors, CSharpCodeStyleOptions.NeverWithSuggestionEnforcement), + SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedProperties, CSharpCodeStyleOptions.NeverWithSuggestionEnforcement), + SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedIndexers, CSharpCodeStyleOptions.NeverWithSuggestionEnforcement)); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] public async Task TestUseExpressionBody1() @@ -212,8 +215,8 @@ await TestInRegularAndScriptAsync( int Foo { get [|=>|] Bar(); - } - }", + } +}", @"class C { int Foo @@ -223,7 +226,7 @@ int Foo return Bar(); } } -}", options: UseBlockBody); +}", options: UseBlockBodyIncludingPropertiesAndIndexers); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] @@ -246,7 +249,7 @@ int Foo Bar(); } } -}", options: UseBlockBody); +}", options: UseBlockBodyIncludingPropertiesAndIndexers); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] @@ -269,7 +272,7 @@ int Foo throw new NotImplementedException(); } } -}", options: UseBlockBody); +}", options: UseBlockBodyIncludingPropertiesAndIndexers); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] @@ -292,7 +295,7 @@ int Foo throw new NotImplementedException(); // comment } } -}", ignoreTrivia: false, options: UseBlockBody); +}", ignoreTrivia: false, options: UseBlockBodyIncludingPropertiesAndIndexers); } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs index 0d3ed2d76ff01..8629b5840e255 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Refactoring/UseExpressionBodyForAccessorsRefactoringTests.cs @@ -124,31 +124,22 @@ await TestMissingAsync( } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestNotOfferedIfUserPrefersBlockBodiesAndInExpressionBody2() - { - await TestMissingAsync( -@"class C -{ - int Foo { get => [||]Bar(); } -}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody() + public async Task TestOfferedForPropertyIfUserPrefersBlockPropertiesAndHasBlockProperty() { await TestInRegularAndScript1Async( @"class C { int Foo { get => [||]Bar(); } }", + @"class C { - int Foo { get { return Bar(); } } -}", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); + int Foo => Bar(); +}", parameters: new TestParameters(options: UseBlockBodyForAccessors_BlockBodyForProperties)); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] - public async Task TestOfferedIfUserPrefersExpressionBodiesAndInExpressionBody2() + public async Task TestOfferForPropertyIfPropertyPrefersBlockButCouldBecomeExpressionBody() { await TestInRegularAndScript1Async( @"class C @@ -157,7 +148,7 @@ await TestInRegularAndScript1Async( }", @"class C { - int Foo { get { return Bar(); } } + int Foo => Bar(); }", parameters: new TestParameters(options: UseExpressionBodyForAccessors_BlockBodyForProperties)); } } diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs index 26954810c2cb7..6b499650a4dda 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyForPropertiesHelper.cs @@ -73,7 +73,7 @@ protected override bool TryConvertToExpressionBody( } var getAccessor = GetSingleGetAccessor(declaration.AccessorList); - if (getAccessor.ExpressionBody != null && + if (getAccessor?.ExpressionBody != null && BlockSyntaxExtensions.MatchesPreference(getAccessor.ExpressionBody.Expression, conversionPreference)) { arrowExpression = SyntaxFactory.ArrowExpressionClause(getAccessor.ExpressionBody.Expression); diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index b5d7004634ed7..0ffa11a3fe0ee 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -79,7 +79,7 @@ public override Location GetDiagnosticLocation(SyntaxNode declaration) protected virtual Location GetDiagnosticLocation(TDeclaration declaration) => this.GetBody(declaration).Statements[0].GetLocation(); - public virtual bool CanOfferUseExpressionBody( + public bool CanOfferUseExpressionBody( OptionSet optionSet, TDeclaration declaration, bool forAnalyzer) { var preference = optionSet.GetOption(this.Option).Value; From f0f77ed7502cfb6cba86a2fd59033fb41398d09b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:30:58 -0700 Subject: [PATCH 082/214] Pool intermediate array. --- .../SymbolTree/SymbolTreeInfo_Source.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 51d104b1e9ce4..02eab56740680 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -58,12 +58,20 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project var compilationOptionsChecksum = ChecksumCache.GetOrCreate(project.CompilationOptions, _ => serializer.CreateChecksum(project.CompilationOptions, cancellationToken)); var parseOptionsChecksum = ChecksumCache.GetOrCreate(project.ParseOptions, _ => serializer.CreateChecksum(project.ParseOptions, cancellationToken)); - var allChecksums = textChecksums.ToList(); - allChecksums.Add(compilationOptionsChecksum); - allChecksums.Add(parseOptionsChecksum); + var allChecksums = ArrayBuilder.GetInstance(); + try + { + allChecksums.AddRange(textChecksums); + allChecksums.Add(compilationOptionsChecksum); + allChecksums.Add(parseOptionsChecksum); - var checksum = Checksum.Create(nameof(SymbolTreeInfo), allChecksums); - return checksum; + var checksum = Checksum.Create(nameof(SymbolTreeInfo), allChecksums); + return checksum; + } + finally + { + allChecksums.Free(); + } } internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( From 030dffce1b66b6a6f57bbf23ebe516d7a1b53971 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:32:23 -0700 Subject: [PATCH 083/214] Do waiting only when data is necessary to give the most time to do things concurrently. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 02eab56740680..603999df8ab85 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -54,9 +54,9 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project return serializer.CreateChecksum(text, cancellationToken); }); - var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); var compilationOptionsChecksum = ChecksumCache.GetOrCreate(project.CompilationOptions, _ => serializer.CreateChecksum(project.CompilationOptions, cancellationToken)); var parseOptionsChecksum = ChecksumCache.GetOrCreate(project.ParseOptions, _ => serializer.CreateChecksum(project.ParseOptions, cancellationToken)); + var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); var allChecksums = ArrayBuilder.GetInstance(); try From 29da1667b7394bd3abe8f6a882e1def539f68326 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:34:35 -0700 Subject: [PATCH 084/214] Grab the text checksum state off of the document checksum state. --- .../FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index e141a9ae8974d..3c99216793b57 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -81,11 +81,10 @@ public static async Task GetChecksumAsync( // text of the document changes, or the ParseOptions change. So we get the checksums // for both of those, and merge them together to make the final checksum. - var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); - var textChecksum = Checksum.Create(WellKnownSynchronizationKinds.SourceText, text.GetChecksum()); + var documentChecksumState = await document.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + var textChecksum = documentChecksumState.Text; var parseOptions = document.Project.ParseOptions; - var serializer = new Serializer(document.Project.Solution.Workspace); var parseOptionsChecksum = ChecksumCache.GetOrCreate( parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); From cee52c0ea61ff1edc08ffe5b0ecba155b619d286 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:38:54 -0700 Subject: [PATCH 085/214] Don't recompute checksums that we already have. --- .../FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 603999df8ab85..3993bef464d00 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -47,15 +47,16 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project // the text of any document changes, or if options for the project change. So we build our // checksum out of that data. var serializer = new Serializer(project.Solution.Workspace); + var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); var textChecksumsTasks = project.Documents.Select(async d => { - var text = await d.GetTextAsync(cancellationToken).ConfigureAwait(false); - return serializer.CreateChecksum(text, cancellationToken); + var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + return documentStateChecksum.Text; }); - var compilationOptionsChecksum = ChecksumCache.GetOrCreate(project.CompilationOptions, _ => serializer.CreateChecksum(project.CompilationOptions, cancellationToken)); - var parseOptionsChecksum = ChecksumCache.GetOrCreate(project.ParseOptions, _ => serializer.CreateChecksum(project.ParseOptions, cancellationToken)); + var compilationOptionsChecksum = projectStateChecksums.CompilationOptions; + var parseOptionsChecksum = projectStateChecksums.ParseOptions; var textChecksums = await Task.WhenAll(textChecksumsTasks).ConfigureAwait(false); var allChecksums = ArrayBuilder.GetInstance(); From 475c2095909e55a9d0a9b2881a1f1552eac2579d Mon Sep 17 00:00:00 2001 From: David Kean Date: Tue, 11 Apr 2017 12:41:59 +1000 Subject: [PATCH 086/214] Debugger displays for command-line items --- .../Core/Portable/CommandLine/CommandLineAnalyzerReference.cs | 2 ++ src/Compilers/Core/Portable/CommandLine/CommandLineReference.cs | 1 + .../Core/Portable/CommandLine/CommandLineSourceFile.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineAnalyzerReference.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineAnalyzerReference.cs index 19f32d7c7ce4e..546748dcb3080 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineAnalyzerReference.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineAnalyzerReference.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.Diagnostics; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis @@ -8,6 +9,7 @@ namespace Microsoft.CodeAnalysis /// /// Describes a command line analyzer assembly specification. /// + [DebuggerDisplay("{FilePath,nq}")] public struct CommandLineAnalyzerReference : IEquatable { private readonly string _path; diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineReference.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineReference.cs index 79476fc43ab18..c37a180c209b8 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineReference.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineReference.cs @@ -9,6 +9,7 @@ namespace Microsoft.CodeAnalysis /// /// Describes a command line metadata reference (assembly or netmodule) specification. /// + [DebuggerDisplay("{Reference,nq}")] public struct CommandLineReference : IEquatable { private readonly string _reference; diff --git a/src/Compilers/Core/Portable/CommandLine/CommandLineSourceFile.cs b/src/Compilers/Core/Portable/CommandLine/CommandLineSourceFile.cs index dcb0ff3b34758..c4a59dfecc090 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommandLineSourceFile.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommandLineSourceFile.cs @@ -7,6 +7,7 @@ namespace Microsoft.CodeAnalysis /// /// Describes a source file specification stored on command line arguments. /// + [DebuggerDisplay("{Path,nq}")] public struct CommandLineSourceFile { private readonly string _path; From 5228fd8154fee47465779e6786afaa38f5fc73d7 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Tue, 2 May 2017 13:51:33 -0700 Subject: [PATCH 087/214] Consider name when matching hierarchy items to projects Our support for showing nodes for analyzers and diagnostics in the Solution Explorer depends on us being able to map from an `IVsHierarchy` and item ID representing a project node to the corresponding Roslyn project in the workspace. In Dev14 this was relatively easy, as we could simply check each `IVsHierarchy` held by a Roslyn project for reference equality with the `IVsHierarchy` for the project in Solution Explorer. This approach doesn't work in Dev15 for CPS-based projects. Because of multi-targeting there is a one-to-many mapping from a project in Solution Explorer to projects in the Roslyn workspace; each project in the Roslyn workspace holds on to a different, target-framework-specific `IVsHierarchy` that comes from the project system and is never seen by Solution Explorer. To handle this I switched to using the "canonical name" and target framework to match things up. It turns out this breaks the legacy project system when two projects are in the same folder--they both report the _directory path_ as their canonical name, rather than their full path. The code to map from Solution Explorer nodes to Roslyn projects thus ends up finding two matching projects where it expected only one. The end result is that the project loads, but when you try to expand it in Solution Explorer you get no children. To fix this we need to check both the canonical name, and the regular name. Even if two projects share the same directory, their names will necessarily be different. --- .../SolutionExplorerShim/HierarchyItemToProjectIdMap.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs b/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs index fb1cc5e0dd9b8..0e53ee3b146f5 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs @@ -32,7 +32,8 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo var nestedHierarchy = hierarchyItem.HierarchyIdentity.NestedHierarchy; var nestedHierarchyId = hierarchyItem.HierarchyIdentity.NestedItemID; - if (!nestedHierarchy.TryGetCanonicalName(nestedHierarchyId, out string nestedCanonicalName)) + if (!nestedHierarchy.TryGetCanonicalName(nestedHierarchyId, out string nestedCanonicalName) + || !nestedHierarchy.TryGetItemName(nestedHierarchyId, out string nestedName)) { projectId = default(ProjectId); return false; @@ -42,7 +43,9 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo .Where(p => { if (p.Hierarchy.TryGetCanonicalName((uint)VSConstants.VSITEMID.Root, out string projectCanonicalName) - && projectCanonicalName.Equals(nestedCanonicalName, System.StringComparison.OrdinalIgnoreCase)) + && p.Hierarchy.TryGetItemName((uint)VSConstants.VSITEMID.Root, out string projectName) + && projectCanonicalName.Equals(nestedCanonicalName, System.StringComparison.OrdinalIgnoreCase) + && projectName.Equals(nestedName)) { if (targetFrameworkMoniker == null) { From 5c41be053a1f1cef1697422a6bc6100f1e09aec8 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 14:07:13 -0700 Subject: [PATCH 088/214] Sort usings. --- src/Compilers/CSharp/Portable/Binder/Binder.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.cs b/src/Compilers/CSharp/Portable/Binder/Binder.cs index e40be9c0c6507..acbcb7bd1b434 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.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; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -7,7 +8,6 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Roslyn.Utilities; -using System; namespace Microsoft.CodeAnalysis.CSharp { @@ -807,4 +807,4 @@ TreeDumperNode DumpAncestors() } } } -} +} \ No newline at end of file From e0cfb0f16d9a88631c7aa52dce79541bc6fe47e7 Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Fri, 21 Apr 2017 13:58:06 -0700 Subject: [PATCH 089/214] Ensure linked files are kept in sync in the workspace Fixes https://devdiv.visualstudio.com/DevDiv/_workitems?id=209299 For additional documents, this makes no semantic change. For regular documents, there are slight semantic changes. Previously, each changed document *incrementally* caused a synchronous `OnDocumentTextChanged` call (with Workspace.CurrentSolution only partially updated, leaving linked files with content discrepancies) and then an asynchronous `RaiseWorkspaceChangedEventAsync` (again with Workspace.CurrentSolution only partially updated). Now, all of the synchronous `OnDocumentTextChange` events happen after the Workspace.CurrentSolution is fully updated, and the `RaiseWorkspaceChangedEventAsync` calls to indicate that a document was updated actually contain before/after solutions with *all* of the linked files updated. --- .../Core/Portable/Workspace/Workspace.cs | 91 +++++++++++++++---- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Workspace.cs b/src/Workspaces/Core/Portable/Workspace/Workspace.cs index 110ac003a3424..cad6564136df1 100644 --- a/src/Workspaces/Core/Portable/Workspace/Workspace.cs +++ b/src/Workspaces/Core/Portable/Workspace/Workspace.cs @@ -830,35 +830,92 @@ protected internal void OnDocumentInfoChanged(DocumentId documentId, DocumentInf /// protected internal void OnDocumentTextChanged(DocumentId documentId, SourceText newText, PreservationMode mode) { - using (_serializationLock.DisposableWait()) - { - CheckDocumentIsInCurrentSolution(documentId); - - var oldSolution = this.CurrentSolution; - var newSolution = this.SetCurrentSolution(oldSolution.WithDocumentText(documentId, newText, mode)); - - var newDocument = newSolution.GetDocument(documentId); - this.OnDocumentTextChanged(newDocument); - - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.DocumentChanged, oldSolution, newSolution, documentId: documentId); - } + OnAnyDocumentTextChanged( + documentId, + newText, + mode, + CheckDocumentIsInCurrentSolution, + (solution, docId) => solution.GetRelatedDocumentIds(docId), + (solution, docId, text, preservationMode) => solution.WithDocumentText(docId, text, preservationMode), + WorkspaceChangeKind.DocumentChanged, + isCodeDocument: true); } /// /// Call this method when the text of a document is updated in the host environment. /// protected internal void OnAdditionalDocumentTextChanged(DocumentId documentId, SourceText newText, PreservationMode mode) + { + OnAnyDocumentTextChanged( + documentId, + newText, + mode, + CheckAdditionalDocumentIsInCurrentSolution, + (solution, docId) => ImmutableArray.Create(docId), + (solution, docId, text, preservationMode) => solution.WithAdditionalDocumentText(docId, text, preservationMode), + WorkspaceChangeKind.AdditionalDocumentChanged, + isCodeDocument: false); + } + + /// + /// When a s text is changed, we need to make sure all of the linked + /// files also have their content updated in the new solution before applying it to the + /// workspace to avoid the workspace having solutions with linked files where the contents + /// do not match. + /// + private void OnAnyDocumentTextChanged( + DocumentId documentId, + SourceText newText, + PreservationMode mode, + Action checkIsInCurrentSolution, + Func> getRelatedDocuments, + Func updateSolutionWithText, + WorkspaceChangeKind changeKind, + bool isCodeDocument) { using (_serializationLock.DisposableWait()) { - CheckAdditionalDocumentIsInCurrentSolution(documentId); + checkIsInCurrentSolution(documentId); - var oldSolution = this.CurrentSolution; - var newSolution = this.SetCurrentSolution(oldSolution.WithAdditionalDocumentText(documentId, newText, mode)); + var originalSolution = CurrentSolution; + var updatedSolution = CurrentSolution; + var previousSolution = updatedSolution; - var newDocument = newSolution.GetAdditionalDocument(documentId); + var linkedDocuments = getRelatedDocuments(updatedSolution, documentId); + var updatedDocumentIds = new List(); - this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.AdditionalDocumentChanged, oldSolution, newSolution, documentId: documentId); + foreach (var linkedDocument in linkedDocuments) + { + previousSolution = updatedSolution; + updatedSolution = updateSolutionWithText(updatedSolution, linkedDocument, newText, mode); + if (previousSolution != updatedSolution) + { + updatedDocumentIds.Add(linkedDocument); + } + } + + if (updatedDocumentIds.Count > 0) + { + var newSolution = SetCurrentSolution(updatedSolution); + + if (isCodeDocument) + { + foreach (var updatedDocumentId in updatedDocumentIds) + { + var newDocument = newSolution.GetDocument(updatedDocumentId); + OnDocumentTextChanged(newDocument); + } + } + + foreach (var updatedDocumentInfo in updatedDocumentIds) + { + RaiseWorkspaceChangedEventAsync( + changeKind, + originalSolution, + newSolution, + documentId: updatedDocumentInfo); + } + } } } From aefe9c968d391ac12c944ab3e0a15c4db161ede6 Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Tue, 2 May 2017 14:08:53 -0700 Subject: [PATCH 090/214] TestLinkedFilesStayInSync unit test --- .../CSharpTest/Workspaces/WorkspaceTests.cs | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs index a14156a13a993..7ecdff7862988 100644 --- a/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs +++ b/src/EditorFeatures/CSharpTest/Workspaces/WorkspaceTests.cs @@ -1,6 +1,8 @@ // 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.Composition; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -11,11 +13,10 @@ using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Composition; using Microsoft.VisualStudio.Text; using Roslyn.Test.Utilities; using Xunit; -using System.Composition; -using Microsoft.VisualStudio.Composition; namespace Microsoft.CodeAnalysis.UnitTests.Workspaces { @@ -1003,5 +1004,51 @@ public void TestAdditionalFile_AddRemove_FromProject() Assert.Equal("original.config", workspace.CurrentSolution.GetProject(project1.Id).AdditionalDocuments.Single().Name); } } + + [Fact, WorkItem(209299, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=209299")] + public async Task TestLinkedFilesStayInSync() + { + var originalText = "class Program1 { }"; + var updatedText = "class Program2 { }"; + + var input = $@" + + + { originalText } + + + + +"; + + using (var workspace = TestWorkspace.Create(input, exportProvider: s_exportProvider.Value)) + { + var eventArgs = new List(); + + workspace.WorkspaceChanged += (s, e) => + { + Assert.Equal(WorkspaceChangeKind.DocumentChanged, e.Kind); + eventArgs.Add(e); + }; + + var originalDocumentId = workspace.GetOpenDocumentIds().Single(id => !workspace.GetTestDocument(id).IsLinkFile); + var linkedDocumentId = workspace.GetOpenDocumentIds().Single(id => workspace.GetTestDocument(id).IsLinkFile); + + workspace.GetTestDocument(originalDocumentId).Update(SourceText.From("class Program2 { }")); + await WaitForWorkspaceOperationsToComplete(workspace); + + Assert.Equal(2, eventArgs.Count); + AssertEx.SetEqual(workspace.Projects.SelectMany(p => p.Documents).Select(d => d.Id), eventArgs.Select(e => e.DocumentId)); + + Assert.Equal(eventArgs[0].OldSolution, eventArgs[1].OldSolution); + Assert.Equal(eventArgs[0].NewSolution, eventArgs[1].NewSolution); + + Assert.Equal(originalText, (await eventArgs[0].OldSolution.GetDocument(originalDocumentId).GetTextAsync().ConfigureAwait(false)).ToString()); + Assert.Equal(originalText, (await eventArgs[1].OldSolution.GetDocument(originalDocumentId).GetTextAsync().ConfigureAwait(false)).ToString()); + + Assert.Equal(updatedText, (await eventArgs[0].NewSolution.GetDocument(originalDocumentId).GetTextAsync().ConfigureAwait(false)).ToString()); + Assert.Equal(updatedText, (await eventArgs[1].NewSolution.GetDocument(originalDocumentId).GetTextAsync().ConfigureAwait(false)).ToString()); + } + } } } From 23dd5599d59d6260c37d18fcb627f8d562adb324 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 16:27:35 -0700 Subject: [PATCH 091/214] Batch all DesignerAttribute changes --- .../AbstractDesignerAttributeService.cs | 39 +- .../IRemoteDesignerAttributeService.cs | 2 +- .../DesignerAttributeData.cs | 20 ++ .../DesignerAttributeIncrementalAnalyzer.cs | 333 +++++++++++------- ...nerAttributeIncrementalAnalyzerProvider.cs | 4 +- .../DesignerAttributeState.cs | 65 ---- .../VisualStudioWorkspaceImpl.cs | 9 +- .../Core/Def/ServicesVisualStudio.csproj | 2 +- .../Core/Portable/Log/FunctionId.cs | 1 + .../CodeAnalysisService_DesignerAttributes.cs | 38 +- 10 files changed, 264 insertions(+), 249 deletions(-) create mode 100644 src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs delete mode 100644 src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index a2672a62b64ee..689bd7bfe4cc5 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -4,19 +4,20 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; namespace Microsoft.CodeAnalysis.DesignerAttributes { internal struct DesignerAttributeResult { + public string FilePath; public string DesignerAttributeArgument; public bool ContainsErrors; public bool NotApplicable; - public DesignerAttributeResult(string designerAttributeArgument, bool containsErrors, bool notApplicable) + public DesignerAttributeResult(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable) { + FilePath = filePath; DesignerAttributeArgument = designerAttributeArgument; ContainsErrors = containsErrors; NotApplicable = notApplicable; @@ -30,32 +31,6 @@ internal abstract class AbstractDesignerAttributeService : IDesignerAttributeSer protected abstract bool HasAttributesOrBaseTypeOrIsPartial(SyntaxNode typeNode); public async Task ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken) - { - var workspace = document.Project.Solution.Workspace; - - // same service run in both inproc and remote host, but remote host will not have RemoteHostClient service, - // so inproc one will always run - var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); - if (client != null && !document.IsOpen()) - { - // run designer attributes scanner on remote host - // we only run closed files to make open document to have better responsiveness. - // also we cache everything related to open files anyway, no saving by running - // them in remote host - return await ScanDesignerAttributesInRemoteHostAsync(client, document, cancellationToken).ConfigureAwait(false); - } - - return await ScanDesignerAttributesInCurrentProcessAsync(document, cancellationToken).ConfigureAwait(false); - } - - private async Task ScanDesignerAttributesInRemoteHostAsync(RemoteHostClient client, Document document, CancellationToken cancellationToken) - { - return await client.RunCodeAnalysisServiceOnRemoteHostAsync( - document.Project.Solution, nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), - document.Id, cancellationToken).ConfigureAwait(false); - } - - private async Task ScanDesignerAttributesInCurrentProcessAsync(Document document, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); @@ -83,7 +58,7 @@ private async Task ScanDesignerAttributesInCurrentProce { // The DesignerCategoryAttribute doesn't exist. either not applicable or // no idea on design attribute status, just leave things as it is. - return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: true); + return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: true); } } @@ -114,7 +89,7 @@ private async Task ScanDesignerAttributesInCurrentProce if (attribute != null && attribute.ConstructorArguments.Length == 1) { designerAttributeArgument = GetArgumentString(attribute.ConstructorArguments[0]); - return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: false); + return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); } } } @@ -126,7 +101,7 @@ private async Task ScanDesignerAttributesInCurrentProce } } - return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: false); + return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); } private static string GetArgumentString(TypedConstant argument) @@ -141,4 +116,4 @@ private static string GetArgumentString(TypedConstant argument) return ((string)argument.Value).Trim(); } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs index c202d46d8ab44..5b824a6050138 100644 --- a/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs @@ -6,6 +6,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { internal interface IRemoteDesignerAttributeService { - Task ScanDesignerAttributesAsync(DocumentId documentId); + Task ScanDesignerAttributesAsync(ProjectId projectId); } } diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs new file mode 100644 index 0000000000000..b3f80b233e6dc --- /dev/null +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs @@ -0,0 +1,20 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.DesignerAttributes; + +namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute +{ + internal class DesignerAttributeData + { + public readonly VersionStamp SemanticVersion; + public readonly ImmutableDictionary PathToResult; + + public DesignerAttributeData(VersionStamp semanticVersion, ImmutableDictionary pathToResult) + { + SemanticVersion = semanticVersion; + PathToResult = pathToResult; + } + } +} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index 15f0c7613c35e..a31c9f9595c99 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis; @@ -9,10 +10,12 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; +using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.Designer.Interfaces; @@ -24,10 +27,12 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribu { internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAffinitizedObject, IIncrementalAnalyzer { + private const string StreamName = ""; + private const string FormatVersion = "3"; + private readonly IForegroundNotificationService _notificationService; private readonly IServiceProvider _serviceProvider; - private readonly DesignerAttributeState _state; private readonly IAsynchronousOperationListener _listener; /// @@ -48,13 +53,6 @@ public DesignerAttributeIncrementalAnalyzer( _notificationService = notificationService; _listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.DesignerAttribute); - _state = new DesignerAttributeState(); - } - - public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) - { - _state.Remove(document.Id); - return _state.PersistAsync(document, new Data(VersionStamp.Default, VersionStamp.Default, designerAttributeArgument: null), cancellationToken); } public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e) @@ -62,133 +60,248 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs return false; } - public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) { - Contract.ThrowIfFalse(document.IsFromPrimaryBranch()); - + Contract.ThrowIfFalse(project.IsFromPrimaryBranch()); cancellationToken.ThrowIfCancellationRequested(); - if (!document.Project.Solution.Workspace.Options.GetOption(InternalFeatureOnOffOptions.DesignerAttributes)) + var vsWorkspace = project.Solution.Workspace as VisualStudioWorkspaceImpl; + if (vsWorkspace == null) { return; } - // use tree version so that things like compiler option changes are considered - var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false); - var projectVersion = await document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); - var semanticVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - var existingData = await _state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false); - if (existingData != null) + if (!vsWorkspace.Options.GetOption(InternalFeatureOnOffOptions.DesignerAttributes)) { - // check whether we can use the data as it is (can happen when re-using persisted data from previous VS session) - if (CheckVersions(document, textVersion, projectVersion, semanticVersion, existingData)) - { - RegisterDesignerAttribute(document, existingData.DesignerAttributeArgument); - return; - } + return; } - var result = await ScanDesignerAttributesOnRemoteHostIfPossibleAsync(document, cancellationToken).ConfigureAwait(false); - if (result.NotApplicable) + // CPS projects do not support designer attributes. So we just skip these projects entirely. + var isCPSProject = await Task.Factory.StartNew( + () => vsWorkspace.IsCPSProject(project), + cancellationToken, + TaskCreationOptions.None, + this.ForegroundTaskScheduler).ConfigureAwait(false); + + if (isCPSProject) { - _state.Remove(document.Id); return; } - // we checked all types in the document, but couldn't find designer attribute, but we can't say this document doesn't have designer attribute - // if the document also contains some errors. - var designerAttributeArgumentOpt = result.ContainsErrors ? new Optional() : new Optional(result.DesignerAttributeArgument); - await RegisterDesignerAttributeAndSaveStateAsync(document, textVersion, semanticVersion, designerAttributeArgumentOpt, cancellationToken).ConfigureAwait(false); + var pathToResult = await TryAnalyzeProjectInRemoteProcessAsync(project, cancellationToken).ConfigureAwait(false); + if (pathToResult == null) + { + pathToResult = await TryAnalyzeProjectInCurrentProcessAsync(project, cancellationToken).ConfigureAwait(false); + } + + cancellationToken.ThrowIfCancellationRequested(); + RegisterDesignerAttributes(project, pathToResult); } - private async Task ScanDesignerAttributesOnRemoteHostIfPossibleAsync(Document document, CancellationToken cancellationToken) + private async Task> TryAnalyzeProjectInCurrentProcessAsync( + Project project, CancellationToken cancellationToken) { - var service = document.GetLanguageService(); - if (service == null) + // use tree version so that things like compiler option changes are considered + var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + var designerAttributeData = await ReadExistingDataAsync(project, cancellationToken).ConfigureAwait(false); + + if (designerAttributeData == null || + !project.CanReusePersistedDependentSemanticVersion(projectVersion, semanticVersion, designerAttributeData.SemanticVersion)) { - return new DesignerAttributeResult(designerAttributeArgument: null, containsErrors: true, notApplicable: true); + designerAttributeData = await ComputeAndPersistDesignerAttributeDataAsync( + project, semanticVersion, cancellationToken).ConfigureAwait(false); } - return await service.ScanDesignerAttributesAsync(document, cancellationToken).ConfigureAwait(false); + return designerAttributeData.PathToResult; } - private bool CheckVersions( - Document document, VersionStamp textVersion, VersionStamp dependentProjectVersion, VersionStamp dependentSemanticVersion, Data existingData) + private async Task ComputeAndPersistDesignerAttributeDataAsync( + Project project, VersionStamp semanticVersion, CancellationToken cancellationToken) { - // first check full version to see whether we can reuse data in same session, if we can't, check timestamp only version to see whether - // we can use it cross-session. - return document.CanReusePersistedTextVersion(textVersion, existingData.TextVersion) && - document.Project.CanReusePersistedDependentSemanticVersion(dependentProjectVersion, dependentSemanticVersion, existingData.SemanticVersion); - } + var service = project.LanguageServices.GetService(); - private async Task RegisterDesignerAttributeAndSaveStateAsync( - Document document, VersionStamp textVersion, VersionStamp semanticVersion, Optional designerAttributeArgumentOpt, CancellationToken cancellationToken) - { - if (!designerAttributeArgumentOpt.HasValue) + var builder = ImmutableDictionary.CreateBuilder(); + foreach (var document in project.Documents) { - // no value means it couldn't determine whether this document has designer attribute or not. - // one of such case is when base type is error type. - return; + var result = await service.ScanDesignerAttributesAsync(document, cancellationToken).ConfigureAwait(false); + builder[document.FilePath] = result; } - var data = new Data(textVersion, semanticVersion, designerAttributeArgumentOpt.Value); - await _state.PersistAsync(document, data, cancellationToken).ConfigureAwait(false); - - RegisterDesignerAttribute(document, designerAttributeArgumentOpt.Value); + var data = new DesignerAttributeData(semanticVersion, builder.ToImmutable()); + PersistData(project, data, cancellationToken); + return data; } - private void RegisterDesignerAttribute(Document document, string designerAttributeArgument) + private async Task ReadExistingDataAsync( + Project project, CancellationToken cancellationToken) { - var workspace = document.Project.Solution.Workspace as VisualStudioWorkspaceImpl; - if (workspace == null) + try { - return; + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) + using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) + { + if (reader != null) + { + var version = reader.ReadString(); + if (version == FormatVersion) + { + var semanticVersion = VersionStamp.ReadFrom(reader); + + var resultCount = reader.ReadInt32(); + var builder = ImmutableDictionary.CreateBuilder(); + + for (var i = 0; i < resultCount; i++) + { + var filePath = reader.ReadString(); + var attribute = reader.ReadString(); + var containsErrors = reader.ReadBoolean(); + var notApplicable = reader.ReadBoolean(); + + builder[filePath] = new DesignerAttributeResult(filePath, attribute, containsErrors, notApplicable); + } + + return new DesignerAttributeData(semanticVersion, builder.ToImmutable()); + } + } + } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. } - var documentId = document.Id; - _notificationService.RegisterNotification(() => + return null; + } + + private void PersistData( + Project project, DesignerAttributeData data, CancellationToken cancellationToken) + { + try { - var vsDocument = workspace.GetHostDocument(documentId); - if (vsDocument == null) + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = SerializableBytes.CreateWritableStream()) + using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { - return; + writer.WriteString(FormatVersion); + data.SemanticVersion.WriteTo(writer); + + writer.WriteInt32(data.PathToResult.Count); + + foreach (var kvp in data.PathToResult) + { + var result = kvp.Value; + writer.WriteString(result.FilePath); + writer.WriteString(result.DesignerAttributeArgument); + writer.WriteBoolean(result.ContainsErrors); + writer.WriteBoolean(result.NotApplicable); + } } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. + } + } - uint itemId = vsDocument.GetItemId(); - if (itemId == (uint)VSConstants.VSITEMID.Nil) + private async Task> TryAnalyzeProjectInRemoteProcessAsync(Project project, CancellationToken cancellationToken) + { + using (var session = await TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false)) + { + if (session == null) { - // it is no longer part of the solution - return; + return null; } - if (ErrorHandler.Succeeded(vsDocument.Project.Hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue))) + var serializedResults = await session.InvokeAsync( + nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), project.Id).ConfigureAwait(false); + + var data = serializedResults.ToImmutableDictionary(kvp => kvp.FilePath); + return data; + } + } + + private static async Task TryGetRemoteSessionAsync( + Solution solution, CancellationToken cancellationToken) + { + var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false); + if (client == null) + { + return null; + } + + return await client.TryCreateCodeAnalysisServiceSessionAsync( + solution, cancellationToken).ConfigureAwait(false); + } + + private void RegisterDesignerAttributes( + Project project, ImmutableDictionary pathToResult) + { + _notificationService.RegisterNotification(() => + { + foreach (var document in project.Documents) { - var currentStringValue = string.IsNullOrEmpty(currentValue as string) ? null : (string)currentValue; - if (string.Equals(currentStringValue, designerAttributeArgument, StringComparison.OrdinalIgnoreCase)) + if (pathToResult.TryGetValue(document.FilePath, out var result)) { - // PERF: Avoid sending the message if the project system already has the current value. - return; + RegisterDesignerAttribute(document, result.DesignerAttributeArgument); } } + }, _listener.BeginAsyncOperation("RegisterDesignerAttribute")); + } + + private void RegisterDesignerAttribute(Document document, string designerAttributeArgument) + { + var workspace = (VisualStudioWorkspaceImpl)document.Project.Solution.Workspace; + + var vsDocument = workspace.GetHostDocument(document.Id); + if (vsDocument == null) + { + return; + } - try + uint itemId = vsDocument.GetItemId(); + if (itemId == (uint)VSConstants.VSITEMID.Nil) + { + // it is no longer part of the solution + return; + } + + if (ErrorHandler.Succeeded(vsDocument.Project.Hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue))) + { + var currentStringValue = string.IsNullOrEmpty(currentValue as string) ? null : (string)currentValue; + if (string.Equals(currentStringValue, designerAttributeArgument, StringComparison.OrdinalIgnoreCase)) { - var designer = GetDesignerFromForegroundThread(); - if (designer != null) - { - designer.RegisterDesignViewAttribute(vsDocument.Project.Hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument); - } + // PERF: Avoid sending the message if the project system already has the current value. + return; } - catch + } + + try + { + var designer = GetDesignerFromForegroundThread(); + if (designer != null) { - // DevDiv # 933717 - // turns out RegisterDesignViewAttribute can throw in certain cases such as a file failed to be checked out by source control - // or IVSHierarchy failed to set a property for this project - // - // just swallow it. don't crash VS. + designer.RegisterDesignViewAttribute(vsDocument.Project.Hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument); } - }, _listener.BeginAsyncOperation("RegisterDesignerAttribute")); + } + catch + { + // DevDiv # 933717 + // turns out RegisterDesignViewAttribute can throw in certain cases such as a file failed to be checked out by source control + // or IVSHierarchy failed to set a property for this project + // + // just swallow it. don't crash VS. + } } private IVSMDDesignerService GetDesignerFromForegroundThread() @@ -204,54 +317,34 @@ private IVSMDDesignerService GetDesignerFromForegroundThread() return _dotNotAccessDirectlyDesigner; } - public void RemoveDocument(DocumentId documentId) - { - _state.Remove(documentId); - } - - private class Data - { - public readonly VersionStamp TextVersion; - public readonly VersionStamp SemanticVersion; - public readonly string DesignerAttributeArgument; - - public Data(VersionStamp textVersion, VersionStamp semanticVersion, string designerAttributeArgument) - { - this.TextVersion = textVersion; - this.SemanticVersion = semanticVersion; - this.DesignerAttributeArgument = designerAttributeArgument; - } - } - #region unused + public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } + => SpecializedTasks.EmptyTask; + + public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken) + => SpecializedTasks.EmptyTask; public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } + => SpecializedTasks.EmptyTask; public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } + => SpecializedTasks.EmptyTask; + + public Task DocumentResetAsync(Document document, CancellationToken cancellationToken) + => SpecializedTasks.EmptyTask; public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken) - { - return SpecializedTasks.EmptyTask; - } + => SpecializedTasks.EmptyTask; - public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken) + public void RemoveDocument(DocumentId documentId) { - return SpecializedTasks.EmptyTask; } public void RemoveProject(ProjectId projectId) { } + #endregion } -} +} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs index 6386228a19eea..68e459f7602c6 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzerProvider.cs @@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribu [ExportIncrementalAnalyzerProvider(Name, new[] { WorkspaceKind.Host }), Shared] internal class DesignerAttributeIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider { - public const string Name = "DesignerAttributeIncrementalAnalyzerProvider"; + public const string Name = nameof(DesignerAttributeIncrementalAnalyzerProvider); private readonly IServiceProvider _serviceProvider; private readonly IForegroundNotificationService _notificationService; @@ -36,4 +36,4 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace) return new DesignerAttributeIncrementalAnalyzer(_serviceProvider, _notificationService, _asyncListeners); } } -} +} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs deleted file mode 100644 index e5d769f295c2e..0000000000000 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeState.cs +++ /dev/null @@ -1,65 +0,0 @@ -// 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.IO; -using System.Threading; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.SolutionCrawler.State; -using Roslyn.Utilities; - -namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute -{ - internal partial class DesignerAttributeIncrementalAnalyzer : IIncrementalAnalyzer - { - private class DesignerAttributeState : AbstractDocumentAnalyzerState - { - private const string FormatVersion = "1"; - - protected override string StateName - { - get - { - return ""; - } - } - - protected override int GetCount(Data data) - { - return 1; - } - - protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken) - { - using (var reader = ObjectReader.TryGetReader(stream)) - { - if (reader != null) - { - var format = reader.ReadString(); - if (string.Equals(format, FormatVersion, StringComparison.InvariantCulture)) - { - var textVersion = VersionStamp.ReadFrom(reader); - var dataVersion = VersionStamp.ReadFrom(reader); - var designerAttributeArgument = reader.ReadString(); - - return new Data(textVersion, dataVersion, designerAttributeArgument); - } - } - } - - return null; - } - - protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken) - { - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) - { - writer.WriteString(FormatVersion); - data.TextVersion.WriteTo(writer); - data.SemanticVersion.WriteTo(writer); - writer.WriteString(data.DesignerAttributeArgument); - } - } - } - } -} diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index 9340d2f5bb24f..d27587ad0e88b 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -187,7 +187,12 @@ internal override bool CanChangeActiveContextDocument } internal override bool CanRenameFilesDuringCodeActions(CodeAnalysis.Project project) + => !IsCPSProject(project); + + internal bool IsCPSProject(CodeAnalysis.Project project) { + _foregroundObject.Value.AssertIsForeground(); + if (this.TryGetHierarchy(project.Id, out var hierarchy)) { // Currently renaming files in CPS projects (i.e. .Net Core) doesn't work proprey. @@ -195,10 +200,10 @@ internal override bool CanRenameFilesDuringCodeActions(CodeAnalysis.Project proj // (despite the DTE interfaces being synchronous). So Roslyn calls the methods // expecting the changes to happen immediately. Because they are deferred in CPS // this causes problems. - return !hierarchy.IsCapabilityMatch("CPS"); + return hierarchy.IsCapabilityMatch("CPS"); } - return true; + return false; } protected override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, CodeAnalysis.Project project) diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 3dced3f823996..18bd652c7bfbd 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -57,6 +57,7 @@ + @@ -494,7 +495,6 @@ - diff --git a/src/Workspaces/Core/Portable/Log/FunctionId.cs b/src/Workspaces/Core/Portable/Log/FunctionId.cs index 328e8278f11ee..608bc297dbe10 100644 --- a/src/Workspaces/Core/Portable/Log/FunctionId.cs +++ b/src/Workspaces/Core/Portable/Log/FunctionId.cs @@ -371,5 +371,6 @@ internal enum FunctionId CodeLens_FindReferenceMethodsAsync, CodeLens_GetFullyQualifiedName, RemoteHostClientService_Restarted, + CodeAnalysisService_GetDesignerAttributesAsync, } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs index be3807b621789..056ae5c10f25b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs @@ -1,11 +1,8 @@ // 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.IO; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DesignerAttributes; using Microsoft.CodeAnalysis.Internal.Log; -using Microsoft.CodeAnalysis.Shared.Extensions; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; namespace Microsoft.CodeAnalysis.Remote @@ -18,35 +15,24 @@ internal partial class CodeAnalysisService : IRemoteDesignerAttributeService /// /// This will be called by ServiceHub/JsonRpc framework /// - public async Task ScanDesignerAttributesAsync(DocumentId documentId) + public async Task ScanDesignerAttributesAsync(ProjectId projectId) { - using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetTodoCommentsAsync, documentId.ProjectId.DebugName, CancellationToken)) + using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetDesignerAttributesAsync, projectId.DebugName, CancellationToken)) { - try - { - var solution = await GetSolutionAsync().ConfigureAwait(false); - var document = solution.GetDocument(documentId); + var solution = await GetSolutionAsync().ConfigureAwait(false); + var project = solution.GetProject(projectId); + var service = project.LanguageServices.GetService(); - var service = document.GetLanguageService(); - if (service != null) - { - // todo comment service supported - return await service.ScanDesignerAttributesAsync(document, CancellationToken).ConfigureAwait(false); - } - } - catch (IOException) - { - // stream to send over result has closed before we - // had chance to check cancellation - } - catch (OperationCanceledException) + var results = new DesignerAttributeResult[project.DocumentIds.Count]; + var index = 0; + foreach (var document in project.Documents) { - // rpc connection has closed. - // this can happen if client side cancelled the - // operation + var result = await service.ScanDesignerAttributesAsync(document, CancellationToken).ConfigureAwait(false); + results[index] = result; + index++; } - return new DesignerAttributeResult(designerAttributeArgument: null, containsErrors: true, notApplicable: true); + return results; } } } From ed668bc5ca617204c57deb1a73d9b573b66dfb2a Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 2 May 2017 16:30:57 -0700 Subject: [PATCH 092/214] Add more unit tests --- .../CSharp/Portable/BoundTree/Expression.cs | 8 +- .../CSharpCompilerSemanticTest.csproj | 1 + ...perationTests_IFieldReferenceExpression.cs | 37 +++ ...tionTests_IParameterReferenceExpression.cs | 115 +++++++-- .../Portable/BoundTree/Expression.vb | 24 -- .../Semantic/BasicCompilerSemanticTest.vbproj | 1 + ...perationTests_IFieldReferenceExpression.vb | 36 +++ ...tionTests_IParameterReferenceExpression.vb | 227 ++++++++++++++++++ 8 files changed, 407 insertions(+), 42 deletions(-) create mode 100644 src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.cs create mode 100644 src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.vb diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index d0404309151b9..af6c74e187dca 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -1419,7 +1419,7 @@ internal partial class BoundDynamicIndexerAccess { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.Add(this.ReceiverOpt).As(); + protected override ImmutableArray Children => this.Arguments.Insert(0, this.ReceiverOpt).As(); public override void Accept(OperationVisitor visitor) { @@ -1567,7 +1567,7 @@ internal partial class BoundPattern internal partial class BoundDeclarationPattern { - protected override ImmutableArray Children => ImmutableArray.Empty; + protected override ImmutableArray Children => ImmutableArray.Create(this.VariableAccess); } internal partial class BoundConstantPattern @@ -1584,8 +1584,6 @@ internal partial class BoundArgListOperator { protected override OperationKind ExpressionKind => OperationKind.None; - protected override ImmutableArray Children => this.Arguments.As(); - public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); @@ -2101,6 +2099,8 @@ internal partial class BoundFixedLocalCollectionInitializer { protected override OperationKind ExpressionKind => OperationKind.None; + protected override ImmutableArray Children => ImmutableArray.Create(this.Expression); + public override void Accept(OperationVisitor visitor) { visitor.VisitNoneOperation(this); diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index e391ca969f34e..fce4ce5d90bab 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -61,6 +61,7 @@ + diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.cs new file mode 100644 index 0000000000000..a32cb3b83ad04 --- /dev/null +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.cs @@ -0,0 +1,37 @@ +// 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.CSharp.Syntax; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests +{ + public partial class IOperationTests : SemanticModelTestBase + { + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void FieldReference_Attribute() + { + string source = @" +using System.Diagnostics; + +class C +{ + private const string field = nameof(field); + + [/**/Conditional(field)/**/] + void M() + { + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'Conditional(field)') + Children(1): IFieldReferenceExpression: System.String C.field (Static) (OperationKind.FieldReferenceExpression, Type: System.String, Constant: ""field"") (Syntax: 'field') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + } +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index 69643cbbb9ace..c891508048a42 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; @@ -137,7 +138,6 @@ public void M(List customers) IReturnStatement (OperationKind.ReturnStatement) (Syntax: 'cust.Name') IPropertyReferenceExpression: System.String Customer.Name { get; set; } (OperationKind.PropertyReferenceExpression, Type: System.String) (Syntax: 'cust.Name') Instance Receiver: IOperation: (OperationKind.None) (Syntax: 'cust') - Children(1): IParameterReferenceExpression: cust (OperationKind.ParameterReferenceExpression, Type: Customer) (Syntax: 'cust') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -380,8 +380,8 @@ public void M(dynamic d, int x) IVariableDeclaration (1 variables) (OperationKind.VariableDeclaration) (Syntax: 'var y /**/;') Variables: Local_1: dynamic y Initializer: IOperation: (OperationKind.None) (Syntax: 'd[x]') - Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') - IParameterReferenceExpression: d (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'd') + Children(2): IParameterReferenceExpression: d (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'd') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -402,9 +402,9 @@ public void M(dynamic x, int y) "; string expectedOperationTree = @" IOperation: (OperationKind.None) (Syntax: 'x.M(y)') - Children(2): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') - IOperation: (OperationKind.None) (Syntax: 'x.M') + Children(2): IOperation: (OperationKind.None) (Syntax: 'x.M') Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -425,8 +425,8 @@ public void M(dynamic x, int y) "; string expectedOperationTree = @" IOperation: (OperationKind.None) (Syntax: 'x(y)') - Children(2): IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') - IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') + Children(2): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: dynamic) (Syntax: 'x') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'y') "; var expectedDiagnostics = DiagnosticDescription.None; @@ -565,8 +565,7 @@ public void M(int x) IOperation: (OperationKind.None) (Syntax: 'case var y ... break;') Children(2): IOperation: (OperationKind.None) (Syntax: 'case var y ... (x >= 10):') Children(2): IOperation: (OperationKind.None) (Syntax: 'var y') - Children(2): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') - IOperation: (OperationKind.None) (Syntax: 'var') + Children(1): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') IBinaryOperatorExpression (BinaryOperationKind.IntegerGreaterThanOrEqual) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'x >= 10') Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') Right: ILiteralExpression (Text: 10) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 10) (Syntax: '10') @@ -598,12 +597,11 @@ public void M(int x) "; string expectedOperationTree = @" IOperation: (OperationKind.None) (Syntax: 'switch (x) ... }') - Children(4): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') + Children(3): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') IOperation: (OperationKind.None) (Syntax: 'case var y ... break;') Children(2): IOperation: (OperationKind.None) (Syntax: 'case var y ... (x >= 10):') Children(2): IOperation: (OperationKind.None) (Syntax: 'var y') - Children(2): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') - IOperation: (OperationKind.None) (Syntax: 'var') + Children(1): ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'var y') IBinaryOperatorExpression (BinaryOperationKind.IntegerGreaterThanOrEqual) (OperationKind.BinaryOperatorExpression, Type: System.Boolean) (Syntax: 'x >= 10') Left: IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.Int32) (Syntax: 'x') Right: ILiteralExpression (Text: 10) (OperationKind.LiteralExpression, Type: System.Int32, Constant: 10) (Syntax: '10') @@ -612,12 +610,101 @@ public void M(int x) Children(2): IOperation: (OperationKind.None) (Syntax: 'default:') Children(1): IOperation: (OperationKind.None) (Syntax: 'default:') IBranchStatement (BranchKind.Break) (OperationKind.BranchStatement) (Syntax: 'break;') - IOperation: (OperationKind.None) (Syntax: 'default:') - Children(1): IOperation: (OperationKind.None) (Syntax: 'default:') "; var expectedDiagnostics = DiagnosticDescription.None; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_UserDefinedLogicalConditionalOperator() + { + string source = @" +class A +{ + public static bool operator true(A o) { return true; } + public static bool operator false(A o) { return false; } +} +class B : A +{ + public static B operator &(B x, B y) { return x; } +} +class C : B +{ + public static C operator |(C x, C y) { return x; } +} +class P +{ + static void M(C x, C y) + { + if (/**/x && y/**/) + { + } + } +} +"; + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'x && y') + Children(2): IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: B) (Syntax: 'x') + IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'x') + IConversionExpression (ConversionKind.Cast, Implicit) (OperationKind.ConversionExpression, Type: B) (Syntax: 'y') + IParameterReferenceExpression: y (OperationKind.ParameterReferenceExpression, Type: C) (Syntax: 'y') +"; + var expectedDiagnostics = DiagnosticDescription.None; + + VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); + } + + [Fact, WorkItem(8884, "https://github.com/dotnet/roslyn/issues/8884")] + public void ParameterReference_NoPiaObjectCreation() + { + var sources0 = @" +using System; +using System.Runtime.InteropServices; + +[assembly: ImportedFromTypeLib(""_.dll"")] +[assembly: Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58257"")] +[ComImport()] +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58277"")] +[CoClass(typeof(C))] +public interface I + { + int P { get; set; } + } +[Guid(""f9c2d51d-4f44-45f0-9eda-c9d599b58278"")] +public class C +{ + public C(object o) + { + } +} +"; + var sources1 = @" +struct S +{ + public I F(object x) + { + return /**/new I(x)/**/; + } +} +"; + var compilation0 = CreateStandardCompilation(sources0); + compilation0.VerifyDiagnostics(); + + var compilation1 = CreateStandardCompilation( + sources1, + references: new[] { MscorlibRef, SystemRef, compilation0.EmitToImageReference(embedInteropTypes: true) }); + + string expectedOperationTree = @" +IOperation: (OperationKind.None) (Syntax: 'new I(x)') +"; + var expectedDiagnostics = new DiagnosticDescription[] { + // (6,25): error CS1729: 'I' does not contain a constructor that takes 1 arguments + // return /**/new I(x)/**/; + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "(x)").WithArguments("I", "1").WithLocation(6, 25) + }; + + VerifyOperationTreeAndDiagnosticsForTest(compilation1, expectedOperationTree, expectedDiagnostics); + } } } \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb index e5eea43a924a7..73ea24e4c27a5 100644 --- a/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb +++ b/src/Compilers/VisualBasic/Portable/BoundTree/Expression.vb @@ -1826,12 +1826,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.UnderlyingLValue) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -1946,12 +1940,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.SourceType) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2458,12 +2446,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.OriginalArgument) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub @@ -2988,12 +2970,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return OperationKind.None End Function - Protected Overrides ReadOnly Property Children As ImmutableArray(Of IOperation) - Get - Return ImmutableArray.Create(Of IOperation)(Me.Expression) - End Get - End Property - Public Overrides Sub Accept(visitor As OperationVisitor) visitor.VisitNoneOperation(Me) End Sub diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index 729cc4fb34930..5869d2fe1a23d 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -97,6 +97,7 @@ + diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.vb new file mode 100644 index 0000000000000..016cd8a89dcb6 --- /dev/null +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IFieldReferenceExpression.vb @@ -0,0 +1,36 @@ +' 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 Microsoft.CodeAnalysis.Semantics +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax +Imports Roslyn.Test.Utilities + +Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.Semantics + + Partial Public Class IOperationTests + Inherits SemanticModelTestBase + + + Public Sub FieldReference_Attribute() + Dim source = 'BIND:"Conditional(field)" + Private Sub M() + End Sub +End Class]]>.Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of AttributeSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + End Class +End Namespace diff --git a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb index 4cc0c70da8f3f..65581f7e4b429 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.vb @@ -100,6 +100,132 @@ IOperation: (OperationKind.None) (Syntax: 'From cust I ... t cust.Name') VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) End Sub + + Public Sub ParameterReference_QueryExpressionAggregateClause() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_QueryExpressionOrderByClause() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_QueryExpressionGroupByClause() + Dim source = .Value + + Dim expectedOperationTree = , )(keySelector As System.Func(Of System.String, ), resultSelector As System.Func(Of , System.Collections.Generic.IEnumerable(Of System.String), )) As System.Collections.Generic.IEnumerable(Of )) (OperationKind.InvocationExpression, Type: System.Collections.Generic.IEnumerable(Of )) (Syntax: 'Group By w ... nto Count()') + Instance Receiver: IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.ConversionExpression, Type: System.Collections.Generic.IEnumerable(Of System.String)) (Syntax: 'y In x') + IOperation: (OperationKind.None) (Syntax: 'y In x') + Children(1): IOperation: (OperationKind.None) (Syntax: 'x') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.String()) (Syntax: 'x') + Arguments(2): IArgument (ArgumentKind.DefaultValue, Matching Parameter: keySelector) (OperationKind.Argument) (Syntax: 'x') + IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.ConversionExpression, Type: System.Func(Of System.String, )) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'x') + Children(1): IOperation: (OperationKind.None) (Syntax: 'Group By w ... nto Count()') + Children(2): IOperation: (OperationKind.None) (Syntax: 'w = x') + Children(1): IParameterReferenceExpression: x (OperationKind.ParameterReferenceExpression, Type: System.String()) (Syntax: 'x') + IOperation: (OperationKind.None) (Syntax: 'z = y') + Children(1): IOperation: (OperationKind.None) (Syntax: 'y') + IArgument (ArgumentKind.DefaultValue, Matching Parameter: resultSelector) (OperationKind.Argument) (Syntax: 'Group By w ... nto Count()') + IConversionExpression (ConversionKind.Basic, Implicit) (OperationKind.ConversionExpression, Type: System.Func(Of , System.Collections.Generic.IEnumerable(Of System.String), )) (Syntax: 'Group By w ... nto Count()') + IOperation: (OperationKind.None) (Syntax: 'Group By w ... nto Count()') + Children(1): IOperation: (OperationKind.None) (Syntax: 'Group By w ... nto Count()') + Children(3): IOperation: (OperationKind.None) (Syntax: 'w') + IOperation: (OperationKind.None) (Syntax: 'z') + IOperation: (OperationKind.None) (Syntax: 'Count()') + Children(1): IOperation: (OperationKind.None) (Syntax: 'Count()') + Children(1): IInvocationExpression ( Function System.Collections.Generic.IEnumerable(Of System.String).Count() As System.Int32) (OperationKind.InvocationExpression, Type: System.Int32) (Syntax: 'Count()') + Instance Receiver: IParameterReferenceExpression: $VB$ItAnonymous (OperationKind.ParameterReferenceExpression, Type: System.Collections.Generic.IEnumerable(Of System.String)) (Syntax: 'Group By w ... nto Count()') +]]>.Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of QueryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + Public Sub ParameterReference_ObjectAndCollectionInitializer() Dim source = + Public Sub ParameterReference_LateAddressOfOperator() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of UnaryExpressionSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_NullableIsTrueOperator() + Dim source = .Value + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = String.Empty + + VerifyOperationTreeAndDiagnosticsForTest(Of MultiLineIfBlockSyntax)(source, expectedOperationTree, expectedDiagnostics) + End Sub + + + Public Sub ParameterReference_NoPiaObjectCreation() + Dim sources0 = + + + + + +Public Interface I + Property P As Integer +End Interface + +Public Class C + Public Sub New(o As Object) + End Sub +End Class +]]> + + Dim sources1 = + + + Dim compilation0 = CreateCompilationWithMscorlib(sources0) + compilation0.AssertTheseDiagnostics() + + ' No errors for /r:_.dll + Dim compilation1 = CreateCompilationWithReferences( + sources1, + references:={MscorlibRef, SystemRef, compilation0.EmitToImageReference(embedInteropTypes:=True)}) + + Dim expectedOperationTree = .Value + + Dim expectedDiagnostics = .Value + + VerifyOperationTreeAndDiagnosticsForTest(Of ObjectCreationExpressionSyntax)(compilation1, "a.vb", expectedOperationTree, expectedDiagnostics) + End Sub End Class End Namespace From df01eb498917d5882a7e97f097146d072c494ba5 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:08:13 -0700 Subject: [PATCH 093/214] Only report changes. --- .../AbstractDesignerAttributeService.cs | 16 ------- .../DesignerAttributeResult.cs | 36 ++++++++++++++++ src/Features/Core/Portable/Features.csproj | 1 + .../DesignerAttributeIncrementalAnalyzer.cs | 42 ++++++++++++++++++- 4 files changed, 78 insertions(+), 17 deletions(-) create mode 100644 src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 689bd7bfe4cc5..fabd53ec09ae1 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -8,22 +8,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { - internal struct DesignerAttributeResult - { - public string FilePath; - public string DesignerAttributeArgument; - public bool ContainsErrors; - public bool NotApplicable; - - public DesignerAttributeResult(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable) - { - FilePath = filePath; - DesignerAttributeArgument = designerAttributeArgument; - ContainsErrors = containsErrors; - NotApplicable = notApplicable; - } - } - internal abstract class AbstractDesignerAttributeService : IDesignerAttributeService { protected abstract bool ProcessOnlyFirstTypeDefined(); diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs new file mode 100644 index 0000000000000..9e2484f2f18b5 --- /dev/null +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs @@ -0,0 +1,36 @@ +// 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; + +namespace Microsoft.CodeAnalysis.DesignerAttributes +{ + internal struct DesignerAttributeResult : IEquatable + { + public string FilePath; + public string DesignerAttributeArgument; + public bool ContainsErrors; + public bool NotApplicable; + + public DesignerAttributeResult(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable) + { + FilePath = filePath; + DesignerAttributeArgument = designerAttributeArgument; + ContainsErrors = containsErrors; + NotApplicable = notApplicable; + } + + public override bool Equals(object obj) + => Equals((DesignerAttributeResult)obj); + + public bool Equals(DesignerAttributeResult other) + { + return FilePath == other.FilePath && + DesignerAttributeArgument == other.DesignerAttributeArgument && + ContainsErrors == other.ContainsErrors && + NotApplicable == other.NotApplicable; + } + + public override int GetHashCode() + => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 92bc7f231b944..2aafbce39b064 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -122,6 +122,7 @@ + diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index a31c9f9595c99..1388837c673fe 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.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.Concurrent; using System.Collections.Generic; using System.Collections.Immutable; using System.Threading; @@ -42,6 +43,9 @@ internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAf /// private IVSMDDesignerService _dotNotAccessDirectlyDesigner; + private readonly ConcurrentDictionary> _lastReportedProjectData = + new ConcurrentDictionary>(); + public DesignerAttributeIncrementalAnalyzer( IServiceProvider serviceProvider, IForegroundNotificationService notificationService, @@ -247,11 +251,24 @@ private async Task> TryAnal private void RegisterDesignerAttributes( Project project, ImmutableDictionary pathToResult) { + // Diff this result against the last result we reported for this project. + // If there are any changes report them all at once to VS. + var lastPathToResult = _lastReportedProjectData.GetOrAdd( + project.Id, ImmutableDictionary.Empty); + + _lastReportedProjectData[project.Id] = pathToResult; + + var difference = GetDifference(lastPathToResult, pathToResult); + if (difference.Count == 0) + { + return; + } + _notificationService.RegisterNotification(() => { foreach (var document in project.Documents) { - if (pathToResult.TryGetValue(document.FilePath, out var result)) + if (difference.TryGetValue(document.FilePath, out var result)) { RegisterDesignerAttribute(document, result.DesignerAttributeArgument); } @@ -259,6 +276,29 @@ private void RegisterDesignerAttributes( }, _listener.BeginAsyncOperation("RegisterDesignerAttribute")); } + private ImmutableDictionary GetDifference( + ImmutableDictionary oldFileToResult, + ImmutableDictionary newFileToResult) + { + var difference = ImmutableDictionary.CreateBuilder(); + + foreach (var newKvp in newFileToResult) + { + // 1) If this result is for a new document. We always need to report it + // 2) If both the old and new data have this result, then report it if it is different. + var filePath = newKvp.Key; + var newResult = newKvp.Value; + + if (!oldFileToResult.TryGetValue(filePath, out var oldResult) || + !newResult.Equals(oldResult)) + { + difference.Add(filePath, newResult); + } + } + + return difference.ToImmutable(); + } + private void RegisterDesignerAttribute(Document document, string designerAttributeArgument) { var workspace = (VisualStudioWorkspaceImpl)document.Project.Solution.Workspace; From 89f0c60248b7691e3b2a7bb0ce81dcdc6e134e4c Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:27:04 -0700 Subject: [PATCH 094/214] Rename types. --- .../AbstractDesignerAttributeService.cs | 135 +++++++++++++++- ...lt.cs => DesignerAttributeDocumentData.cs} | 8 +- .../DesignerAttributeProjectData.cs} | 11 +- .../IDesignerAttributeService.cs | 2 +- .../IRemoteDesignerAttributeService.cs | 2 +- src/Features/Core/Portable/Features.csproj | 3 +- .../DesignerAttributeIncrementalAnalyzer.cs | 150 +++--------------- .../Core/Def/ServicesVisualStudio.csproj | 1 - .../Services/ServiceHubServicesTests.cs | 2 +- .../CodeAnalysisService_DesignerAttributes.cs | 17 +- 10 files changed, 171 insertions(+), 160 deletions(-) rename src/Features/Core/Portable/DesignerAttributes/{DesignerAttributeResult.cs => DesignerAttributeDocumentData.cs} (74%) rename src/{VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs => Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs} (57%) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index fabd53ec09ae1..27c04056e0ed6 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -1,20 +1,29 @@ // 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.Host; using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Microsoft.CodeAnalysis.Versions; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DesignerAttributes { internal abstract class AbstractDesignerAttributeService : IDesignerAttributeService { + private const string StreamName = ""; + private const string FormatVersion = "3"; + protected abstract bool ProcessOnlyFirstTypeDefined(); protected abstract IEnumerable GetAllTopLevelTypeDefined(SyntaxNode root); protected abstract bool HasAttributesOrBaseTypeOrIsPartial(SyntaxNode typeNode); - public async Task ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken) + public async Task ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false); @@ -42,7 +51,7 @@ public async Task ScanDesignerAttributesAsync(Document { // The DesignerCategoryAttribute doesn't exist. either not applicable or // no idea on design attribute status, just leave things as it is. - return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: true); + return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: true); } } @@ -73,7 +82,7 @@ public async Task ScanDesignerAttributesAsync(Document if (attribute != null && attribute.ConstructorArguments.Length == 1) { designerAttributeArgument = GetArgumentString(attribute.ConstructorArguments[0]); - return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); + return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); } } } @@ -85,7 +94,7 @@ public async Task ScanDesignerAttributesAsync(Document } } - return new DesignerAttributeResult(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); + return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false); } private static string GetArgumentString(TypedConstant argument) @@ -99,5 +108,123 @@ private static string GetArgumentString(TypedConstant argument) return ((string)argument.Value).Trim(); } + + internal static async Task> TryAnalyzeProjectInCurrentProcessAsync( + Project project, CancellationToken cancellationToken) + { + var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); + var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); + + // Get whatever data we've current persisted. + var designerAttributeData = await ReadExistingDataAsync(project, cancellationToken).ConfigureAwait(false); + + // If we have no persisted data, or the persisted data is for a previous version of + // the project, then compute the results for the current project snapshot. + if (designerAttributeData == null || + !project.CanReusePersistedDependentSemanticVersion(projectVersion, semanticVersion, designerAttributeData.SemanticVersion)) + { + designerAttributeData = await ComputeAndPersistDesignerAttributeProjectDataAsync( + project, semanticVersion, cancellationToken).ConfigureAwait(false); + } + + return designerAttributeData.PathToDocumentData; + } + + private static async Task ComputeAndPersistDesignerAttributeProjectDataAsync( + Project project, VersionStamp semanticVersion, CancellationToken cancellationToken) + { + var service = project.LanguageServices.GetService(); + + var builder = ImmutableDictionary.CreateBuilder(); + foreach (var document in project.Documents) + { + var result = await service.ScanDesignerAttributesAsync(document, cancellationToken).ConfigureAwait(false); + builder[document.FilePath] = result; + } + + var data = new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); + PersistProjectData(project, data, cancellationToken); + return data; + } + + private static async Task ReadExistingDataAsync( + Project project, CancellationToken cancellationToken) + { + try + { + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) + using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) + { + if (reader != null) + { + var version = reader.ReadString(); + if (version == FormatVersion) + { + var semanticVersion = VersionStamp.ReadFrom(reader); + + var resultCount = reader.ReadInt32(); + var builder = ImmutableDictionary.CreateBuilder(); + + for (var i = 0; i < resultCount; i++) + { + var filePath = reader.ReadString(); + var attribute = reader.ReadString(); + var containsErrors = reader.ReadBoolean(); + var notApplicable = reader.ReadBoolean(); + + builder[filePath] = new DesignerAttributeDocumentData(filePath, attribute, containsErrors, notApplicable); + } + + return new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); + } + } + } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. + } + + return null; + } + + private static void PersistProjectData( + Project project, DesignerAttributeProjectData data, CancellationToken cancellationToken) + { + try + { + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = SerializableBytes.CreateWritableStream()) + using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + { + writer.WriteString(FormatVersion); + data.SemanticVersion.WriteTo(writer); + + writer.WriteInt32(data.PathToDocumentData.Count); + + foreach (var kvp in data.PathToDocumentData) + { + var result = kvp.Value; + writer.WriteString(result.FilePath); + writer.WriteString(result.DesignerAttributeArgument); + writer.WriteBoolean(result.ContainsErrors); + writer.WriteBoolean(result.NotApplicable); + } + } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. + } + } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeDocumentData.cs similarity index 74% rename from src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs rename to src/Features/Core/Portable/DesignerAttributes/DesignerAttributeDocumentData.cs index 9e2484f2f18b5..1ca0ab2c7368b 100644 --- a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeResult.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeDocumentData.cs @@ -4,14 +4,14 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { - internal struct DesignerAttributeResult : IEquatable + internal struct DesignerAttributeDocumentData : IEquatable { public string FilePath; public string DesignerAttributeArgument; public bool ContainsErrors; public bool NotApplicable; - public DesignerAttributeResult(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable) + public DesignerAttributeDocumentData(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable) { FilePath = filePath; DesignerAttributeArgument = designerAttributeArgument; @@ -20,9 +20,9 @@ public DesignerAttributeResult(string filePath, string designerAttributeArgument } public override bool Equals(object obj) - => Equals((DesignerAttributeResult)obj); + => Equals((DesignerAttributeDocumentData)obj); - public bool Equals(DesignerAttributeResult other) + public bool Equals(DesignerAttributeDocumentData other) { return FilePath == other.FilePath && DesignerAttributeArgument == other.DesignerAttributeArgument && diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs similarity index 57% rename from src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs rename to src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs index b3f80b233e6dc..97fad11e601eb 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeData.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs @@ -4,17 +4,18 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.DesignerAttributes; -namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute +namespace Microsoft.CodeAnalysis.DesignerAttributes { - internal class DesignerAttributeData + internal class DesignerAttributeProjectData { public readonly VersionStamp SemanticVersion; - public readonly ImmutableDictionary PathToResult; + public readonly ImmutableDictionary PathToDocumentData; - public DesignerAttributeData(VersionStamp semanticVersion, ImmutableDictionary pathToResult) + public DesignerAttributeProjectData( + VersionStamp semanticVersion, ImmutableDictionary pathToDocumentData) { SemanticVersion = semanticVersion; - PathToResult = pathToResult; + PathToDocumentData = pathToDocumentData; } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/DesignerAttributes/IDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/IDesignerAttributeService.cs index aa76cc91a4316..4a9b923387293 100644 --- a/src/Features/Core/Portable/DesignerAttributes/IDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/IDesignerAttributeService.cs @@ -8,6 +8,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { internal interface IDesignerAttributeService : ILanguageService { - Task ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken); + Task ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken); } } diff --git a/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs index 5b824a6050138..e17055ec67a63 100644 --- a/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/IRemoteDesignerAttributeService.cs @@ -6,6 +6,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { internal interface IRemoteDesignerAttributeService { - Task ScanDesignerAttributesAsync(ProjectId projectId); + Task ScanDesignerAttributesAsync(ProjectId projectId); } } diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 2aafbce39b064..4d5e6720a2477 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -122,7 +122,8 @@ - + + diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index 1388837c673fe..201ac03a3acc4 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs @@ -28,9 +28,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribu { internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAffinitizedObject, IIncrementalAnalyzer { - private const string StreamName = ""; - private const string FormatVersion = "3"; - private readonly IForegroundNotificationService _notificationService; private readonly IServiceProvider _serviceProvider; @@ -43,8 +40,12 @@ internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAf /// private IVSMDDesignerService _dotNotAccessDirectlyDesigner; - private readonly ConcurrentDictionary> _lastReportedProjectData = - new ConcurrentDictionary>(); + /// + /// Keep track of the last results we reported to VS. We can use this to diff future results + /// to report only what actually changed. + /// + private readonly ConcurrentDictionary> _lastReportedProjectData = + new ConcurrentDictionary>(); public DesignerAttributeIncrementalAnalyzer( IServiceProvider serviceProvider, @@ -92,133 +93,22 @@ public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, In return; } + // Try to compute this data in the remote process. If that fails, then compute + // the results in the local process. var pathToResult = await TryAnalyzeProjectInRemoteProcessAsync(project, cancellationToken).ConfigureAwait(false); if (pathToResult == null) { - pathToResult = await TryAnalyzeProjectInCurrentProcessAsync(project, cancellationToken).ConfigureAwait(false); + pathToResult = await AbstractDesignerAttributeService.TryAnalyzeProjectInCurrentProcessAsync( + project, cancellationToken).ConfigureAwait(false); } cancellationToken.ThrowIfCancellationRequested(); - RegisterDesignerAttributes(project, pathToResult); - } - - private async Task> TryAnalyzeProjectInCurrentProcessAsync( - Project project, CancellationToken cancellationToken) - { - // use tree version so that things like compiler option changes are considered - var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false); - var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false); - - var designerAttributeData = await ReadExistingDataAsync(project, cancellationToken).ConfigureAwait(false); - - if (designerAttributeData == null || - !project.CanReusePersistedDependentSemanticVersion(projectVersion, semanticVersion, designerAttributeData.SemanticVersion)) - { - designerAttributeData = await ComputeAndPersistDesignerAttributeDataAsync( - project, semanticVersion, cancellationToken).ConfigureAwait(false); - } - - return designerAttributeData.PathToResult; - } - - private async Task ComputeAndPersistDesignerAttributeDataAsync( - Project project, VersionStamp semanticVersion, CancellationToken cancellationToken) - { - var service = project.LanguageServices.GetService(); - - var builder = ImmutableDictionary.CreateBuilder(); - foreach (var document in project.Documents) - { - var result = await service.ScanDesignerAttributesAsync(document, cancellationToken).ConfigureAwait(false); - builder[document.FilePath] = result; - } - - var data = new DesignerAttributeData(semanticVersion, builder.ToImmutable()); - PersistData(project, data, cancellationToken); - return data; - } - - private async Task ReadExistingDataAsync( - Project project, CancellationToken cancellationToken) - { - try - { - var solution = project.Solution; - var workspace = project.Solution.Workspace; - var storageService = workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) - using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) - using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) - { - if (reader != null) - { - var version = reader.ReadString(); - if (version == FormatVersion) - { - var semanticVersion = VersionStamp.ReadFrom(reader); - - var resultCount = reader.ReadInt32(); - var builder = ImmutableDictionary.CreateBuilder(); - - for (var i = 0; i < resultCount; i++) - { - var filePath = reader.ReadString(); - var attribute = reader.ReadString(); - var containsErrors = reader.ReadBoolean(); - var notApplicable = reader.ReadBoolean(); - - builder[filePath] = new DesignerAttributeResult(filePath, attribute, containsErrors, notApplicable); - } - - return new DesignerAttributeData(semanticVersion, builder.ToImmutable()); - } - } - } - } - catch (Exception e) when (IOUtilities.IsNormalIOException(e)) - { - // Storage APIs can throw arbitrary exceptions. - } - - return null; - } - - private void PersistData( - Project project, DesignerAttributeData data, CancellationToken cancellationToken) - { - try - { - var solution = project.Solution; - var workspace = project.Solution.Workspace; - - var storageService = workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) - using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) - { - writer.WriteString(FormatVersion); - data.SemanticVersion.WriteTo(writer); - - writer.WriteInt32(data.PathToResult.Count); - - foreach (var kvp in data.PathToResult) - { - var result = kvp.Value; - writer.WriteString(result.FilePath); - writer.WriteString(result.DesignerAttributeArgument); - writer.WriteBoolean(result.ContainsErrors); - writer.WriteBoolean(result.NotApplicable); - } - } - } - catch (Exception e) when (IOUtilities.IsNormalIOException(e)) - { - // Storage APIs can throw arbitrary exceptions. - } + // Once we get the current data, diff it and report the results to VS. + RegisterDesignerAttributes(project, pathToResult); } - private async Task> TryAnalyzeProjectInRemoteProcessAsync(Project project, CancellationToken cancellationToken) + private async Task> TryAnalyzeProjectInRemoteProcessAsync(Project project, CancellationToken cancellationToken) { using (var session = await TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false)) { @@ -227,7 +117,7 @@ private async Task> TryAnal return null; } - var serializedResults = await session.InvokeAsync( + var serializedResults = await session.InvokeAsync( nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), project.Id).ConfigureAwait(false); var data = serializedResults.ToImmutableDictionary(kvp => kvp.FilePath); @@ -249,12 +139,12 @@ private async Task> TryAnal } private void RegisterDesignerAttributes( - Project project, ImmutableDictionary pathToResult) + Project project, ImmutableDictionary pathToResult) { // Diff this result against the last result we reported for this project. // If there are any changes report them all at once to VS. var lastPathToResult = _lastReportedProjectData.GetOrAdd( - project.Id, ImmutableDictionary.Empty); + project.Id, ImmutableDictionary.Empty); _lastReportedProjectData[project.Id] = pathToResult; @@ -276,11 +166,11 @@ private void RegisterDesignerAttributes( }, _listener.BeginAsyncOperation("RegisterDesignerAttribute")); } - private ImmutableDictionary GetDifference( - ImmutableDictionary oldFileToResult, - ImmutableDictionary newFileToResult) + private ImmutableDictionary GetDifference( + ImmutableDictionary oldFileToResult, + ImmutableDictionary newFileToResult) { - var difference = ImmutableDictionary.CreateBuilder(); + var difference = ImmutableDictionary.CreateBuilder(); foreach (var newKvp in newFileToResult) { diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 18bd652c7bfbd..a8b66a31ca505 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -57,7 +57,6 @@ - diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index a0ac2a9bedcb0..3cea940b893b3 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -92,7 +92,7 @@ class Test { }"; var solution = workspace.CurrentSolution; - var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync( + var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync( solution, nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), solution.Projects.First().DocumentIds.First(), CancellationToken.None); diff --git a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs index 056ae5c10f25b..f1cfbaa0565c2 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/CodeAnalysisService_DesignerAttributes.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.Linq; using System.Threading.Tasks; using Microsoft.CodeAnalysis.DesignerAttributes; using Microsoft.CodeAnalysis.Internal.Log; @@ -15,24 +16,16 @@ internal partial class CodeAnalysisService : IRemoteDesignerAttributeService /// /// This will be called by ServiceHub/JsonRpc framework /// - public async Task ScanDesignerAttributesAsync(ProjectId projectId) + public async Task ScanDesignerAttributesAsync(ProjectId projectId) { using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetDesignerAttributesAsync, projectId.DebugName, CancellationToken)) { var solution = await GetSolutionAsync().ConfigureAwait(false); var project = solution.GetProject(projectId); - var service = project.LanguageServices.GetService(); + var data = await AbstractDesignerAttributeService.TryAnalyzeProjectInCurrentProcessAsync( + project, CancellationToken).ConfigureAwait(false); - var results = new DesignerAttributeResult[project.DocumentIds.Count]; - var index = 0; - foreach (var document in project.Documents) - { - var result = await service.ScanDesignerAttributesAsync(document, CancellationToken).ConfigureAwait(false); - results[index] = result; - index++; - } - - return results; + return data.Values.ToArray(); } } } From fb11c893582d8b98387483b3b3fcfa8f43560afa Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:28:31 -0700 Subject: [PATCH 095/214] Remove unused usings. --- .../DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index 201ac03a3acc4..fd91f038e76d4 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs @@ -11,14 +11,11 @@ using Microsoft.CodeAnalysis.Editor; using Microsoft.CodeAnalysis.Editor.Shared.Options; using Microsoft.CodeAnalysis.Editor.Shared.Utilities; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Remote; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.TestHooks; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.SolutionCrawler; -using Microsoft.CodeAnalysis.Versions; using Microsoft.VisualStudio.Designer.Interfaces; using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem; using Microsoft.VisualStudio.Shell.Interop; From 5715ac01f3bf0736ce420ebf6d62444d82405a30 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:39:18 -0700 Subject: [PATCH 096/214] move persistence into data class. --- .../AbstractDesignerAttributeService.cs | 88 +----------------- .../DesignerAttributeProjectData.cs | 91 ++++++++++++++++++- 2 files changed, 92 insertions(+), 87 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 27c04056e0ed6..7fc709a837ba3 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -16,9 +16,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes { internal abstract class AbstractDesignerAttributeService : IDesignerAttributeService { - private const string StreamName = ""; - private const string FormatVersion = "3"; - protected abstract bool ProcessOnlyFirstTypeDefined(); protected abstract IEnumerable GetAllTopLevelTypeDefined(SyntaxNode root); protected abstract bool HasAttributesOrBaseTypeOrIsPartial(SyntaxNode typeNode); @@ -116,7 +113,8 @@ internal static async Task ComputeAndPersistDesigne } var data = new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); - PersistProjectData(project, data, cancellationToken); + data.Persist(project, cancellationToken); return data; } - - private static async Task ReadExistingDataAsync( - Project project, CancellationToken cancellationToken) - { - try - { - var solution = project.Solution; - var workspace = project.Solution.Workspace; - - var storageService = workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) - using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) - using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) - { - if (reader != null) - { - var version = reader.ReadString(); - if (version == FormatVersion) - { - var semanticVersion = VersionStamp.ReadFrom(reader); - - var resultCount = reader.ReadInt32(); - var builder = ImmutableDictionary.CreateBuilder(); - - for (var i = 0; i < resultCount; i++) - { - var filePath = reader.ReadString(); - var attribute = reader.ReadString(); - var containsErrors = reader.ReadBoolean(); - var notApplicable = reader.ReadBoolean(); - - builder[filePath] = new DesignerAttributeDocumentData(filePath, attribute, containsErrors, notApplicable); - } - - return new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); - } - } - } - } - catch (Exception e) when (IOUtilities.IsNormalIOException(e)) - { - // Storage APIs can throw arbitrary exceptions. - } - - return null; - } - - private static void PersistProjectData( - Project project, DesignerAttributeProjectData data, CancellationToken cancellationToken) - { - try - { - var solution = project.Solution; - var workspace = project.Solution.Workspace; - - var storageService = workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) - using (var stream = SerializableBytes.CreateWritableStream()) - using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) - { - writer.WriteString(FormatVersion); - data.SemanticVersion.WriteTo(writer); - - writer.WriteInt32(data.PathToDocumentData.Count); - - foreach (var kvp in data.PathToDocumentData) - { - var result = kvp.Value; - writer.WriteString(result.FilePath); - writer.WriteString(result.DesignerAttributeArgument); - writer.WriteBoolean(result.ContainsErrors); - writer.WriteBoolean(result.NotApplicable); - } - } - } - catch (Exception e) when (IOUtilities.IsNormalIOException(e)) - { - // Storage APIs can throw arbitrary exceptions. - } - } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs index 97fad11e601eb..0125d12198963 100644 --- a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs @@ -1,13 +1,20 @@ // 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.Immutable; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.DesignerAttributes; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DesignerAttributes { internal class DesignerAttributeProjectData { + private const string StreamName = ""; + private const string FormatVersion = "3"; + public readonly VersionStamp SemanticVersion; public readonly ImmutableDictionary PathToDocumentData; @@ -17,5 +24,85 @@ public DesignerAttributeProjectData( SemanticVersion = semanticVersion; PathToDocumentData = pathToDocumentData; } + + public static async Task ReadAsync( + Project project, CancellationToken cancellationToken) + { + try + { + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) + using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) + { + if (reader != null) + { + var version = reader.ReadString(); + if (version == FormatVersion) + { + var semanticVersion = VersionStamp.ReadFrom(reader); + + var resultCount = reader.ReadInt32(); + var builder = ImmutableDictionary.CreateBuilder(); + + for (var i = 0; i < resultCount; i++) + { + var filePath = reader.ReadString(); + var attribute = reader.ReadString(); + var containsErrors = reader.ReadBoolean(); + var notApplicable = reader.ReadBoolean(); + + builder[filePath] = new DesignerAttributeDocumentData(filePath, attribute, containsErrors, notApplicable); + } + + return new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); + } + } + } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. + } + + return null; + } + + public void Persist( + Project project, CancellationToken cancellationToken) + { + try + { + var solution = project.Solution; + var workspace = project.Solution.Workspace; + + var storageService = workspace.Services.GetService(); + using (var persistenceService = storageService.GetStorage(solution)) + using (var stream = SerializableBytes.CreateWritableStream()) + using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) + { + writer.WriteString(FormatVersion); + this.SemanticVersion.WriteTo(writer); + + writer.WriteInt32(this.PathToDocumentData.Count); + + foreach (var kvp in this.PathToDocumentData) + { + var result = kvp.Value; + writer.WriteString(result.FilePath); + writer.WriteString(result.DesignerAttributeArgument); + writer.WriteBoolean(result.ContainsErrors); + writer.WriteBoolean(result.NotApplicable); + } + } + } + catch (Exception e) when (IOUtilities.IsNormalIOException(e)) + { + // Storage APIs can throw arbitrary exceptions. + } + } } } \ No newline at end of file From 6ae89de680b9ccb911c5ffc04a04dcd6d146b8a0 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:40:51 -0700 Subject: [PATCH 097/214] Remove unused usings. --- .../DesignerAttributes/AbstractDesignerAttributeService.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 7fc709a837ba3..1238efb38c220 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -1,16 +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; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Host; using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Versions; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.DesignerAttributes { From f1a848e4a0090d8f5ed4a0af88ba9d049f87f093 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 18:42:26 -0700 Subject: [PATCH 098/214] Simplify. --- .../DesignerAttributes/DesignerAttributeProjectData.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs index 0125d12198963..48fb121f9aee8 100644 --- a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs @@ -31,9 +31,8 @@ public static async Task ReadAsync( try { var solution = project.Solution; - var workspace = project.Solution.Workspace; + var storageService = solution.Workspace.Services.GetService(); - var storageService = workspace.Services.GetService(); using (var persistenceService = storageService.GetStorage(solution)) using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) @@ -71,15 +70,13 @@ public static async Task ReadAsync( return null; } - public void Persist( - Project project, CancellationToken cancellationToken) + public void Persist(Project project, CancellationToken cancellationToken) { try { var solution = project.Solution; - var workspace = project.Solution.Workspace; + var storageService = solution.Workspace.Services.GetService(); - var storageService = workspace.Services.GetService(); using (var persistenceService = storageService.GetStorage(solution)) using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) From 0be6440c39d6b8054fd8f83708e6b949277274a3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 19:14:51 -0700 Subject: [PATCH 099/214] Persist data in OOP process. --- .../AbstractDesignerAttributeService.cs | 2 +- .../DesignerAttributeProjectData.cs | 12 +++++++----- .../Workspace/Storage/PersistentStorageService.cs | 8 ++++++++ .../Core/Portable/Workspace/WorkspaceKind.cs | 1 + .../Remote/Core/Services/TemporaryWorkspace.cs | 6 ++---- .../TemporaryWorkspaceOptionsServiceFactory.cs | 2 +- 6 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 1238efb38c220..8fb2bec98fcf6 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -137,7 +137,7 @@ private static async Task ComputeAndPersistDesigne } var data = new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable()); - data.Persist(project, cancellationToken); + await data.PersistAsync(project, cancellationToken).ConfigureAwait(false); return data; } } diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs index 48fb121f9aee8..7923626b336ef 100644 --- a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs @@ -31,9 +31,9 @@ public static async Task ReadAsync( try { var solution = project.Solution; - var storageService = solution.Workspace.Services.GetService(); + var storageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) + using (var persistenceService = storageService.GetStorage(solution, checkBranchId: false)) using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false)) using (var reader = ObjectReader.TryGetReader(stream, cancellationToken)) { @@ -70,14 +70,14 @@ public static async Task ReadAsync( return null; } - public void Persist(Project project, CancellationToken cancellationToken) + public async Task PersistAsync(Project project, CancellationToken cancellationToken) { try { var solution = project.Solution; - var storageService = solution.Workspace.Services.GetService(); + var storageService = (IPersistentStorageService2)solution.Workspace.Services.GetService(); - using (var persistenceService = storageService.GetStorage(solution)) + using (var storage = storageService.GetStorage(solution, checkBranchId: false)) using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken)) { @@ -94,6 +94,8 @@ public void Persist(Project project, CancellationToken cancellationToken) writer.WriteBoolean(result.ContainsErrors); writer.WriteBoolean(result.NotApplicable); } + + await storage.WriteStreamAsync(project, StreamName, stream, cancellationToken).ConfigureAwait(false); } } catch (Exception e) when (IOUtilities.IsNormalIOException(e)) diff --git a/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs b/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs index ec08c7d0dd8ca..1819cd9e7ddbd 100644 --- a/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs +++ b/src/Workspaces/Core/Desktop/Workspace/Storage/PersistentStorageService.cs @@ -167,6 +167,14 @@ private bool SolutionSizeAboveThreshold(Solution solution) return true; } + var workspace = solution.Workspace; + if (workspace.Kind == WorkspaceKind.RemoteWorkspace || + workspace.Kind == WorkspaceKind.RemoteTemporaryWorkspace) + { + // Storage is always available in the remote server. + return true; + } + if (_solutionSizeTracker == null) { return false; diff --git a/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs b/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs index 720e84fcccc5d..0b86dcca007e6 100644 --- a/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs +++ b/src/Workspaces/Core/Portable/Workspace/WorkspaceKind.cs @@ -17,5 +17,6 @@ public static class WorkspaceKind internal const string AnyCodeRoslynWorkspace = nameof(AnyCodeRoslynWorkspace); internal const string RemoteWorkspace = nameof(RemoteWorkspace); + internal const string RemoteTemporaryWorkspace = nameof(RemoteTemporaryWorkspace); } } diff --git a/src/Workspaces/Remote/Core/Services/TemporaryWorkspace.cs b/src/Workspaces/Remote/Core/Services/TemporaryWorkspace.cs index 1104ecc98a206..58d32c9c2fdc3 100644 --- a/src/Workspaces/Remote/Core/Services/TemporaryWorkspace.cs +++ b/src/Workspaces/Remote/Core/Services/TemporaryWorkspace.cs @@ -11,10 +11,8 @@ namespace Microsoft.CodeAnalysis.Remote /// internal class TemporaryWorkspace : Workspace { - public const string WorkspaceKind_TemporaryWorkspace = "TemporaryWorkspace"; - public TemporaryWorkspace(Solution solution) - : base(RoslynServices.HostServices, workspaceKind: TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace) + : base(RoslynServices.HostServices, workspaceKind: WorkspaceKind.RemoteTemporaryWorkspace) { Options = Options.WithChangedOption(CacheOptions.RecoverableTreeLengthThreshold, 0); @@ -22,7 +20,7 @@ public TemporaryWorkspace(Solution solution) } public TemporaryWorkspace(SolutionInfo solutionInfo) - : base(RoslynServices.HostServices, workspaceKind: TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace) + : base(RoslynServices.HostServices, workspaceKind: WorkspaceKind.RemoteTemporaryWorkspace) { Options = Options.WithChangedOption(CacheOptions.RecoverableTreeLengthThreshold, 0); diff --git a/src/Workspaces/Remote/Core/Services/TemporaryWorkspaceOptionsServiceFactory.cs b/src/Workspaces/Remote/Core/Services/TemporaryWorkspaceOptionsServiceFactory.cs index ec90535b93bd2..dc7820f29b9f5 100644 --- a/src/Workspaces/Remote/Core/Services/TemporaryWorkspaceOptionsServiceFactory.cs +++ b/src/Workspaces/Remote/Core/Services/TemporaryWorkspaceOptionsServiceFactory.cs @@ -12,7 +12,7 @@ namespace Microsoft.CodeAnalysis.Remote { - [ExportWorkspaceServiceFactory(typeof(IOptionService), TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace), Shared] + [ExportWorkspaceServiceFactory(typeof(IOptionService), WorkspaceKind.RemoteTemporaryWorkspace), Shared] internal class TemporaryWorkspaceOptionsServiceFactory : IWorkspaceServiceFactory { private readonly ImmutableArray> _providers; From f808369bdd8e9513a7ba1646e5f75bd899404748 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 19:17:31 -0700 Subject: [PATCH 100/214] Set position before writing. --- .../Portable/DesignerAttributes/DesignerAttributeProjectData.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs index 7923626b336ef..d82db11bd93c6 100644 --- a/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs +++ b/src/Features/Core/Portable/DesignerAttributes/DesignerAttributeProjectData.cs @@ -95,6 +95,7 @@ public async Task PersistAsync(Project project, CancellationToken cancellationTo writer.WriteBoolean(result.NotApplicable); } + stream.Position = 0; await storage.WriteStreamAsync(project, StreamName, stream, cancellationToken).ConfigureAwait(false); } } From c9e4269fcf40634c4b7b106ba4f2c71bc83cda55 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 19:35:39 -0700 Subject: [PATCH 101/214] Simplify check. --- .../DesignerAttributes/AbstractDesignerAttributeService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs index 8fb2bec98fcf6..99b6927df2b02 100644 --- a/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs +++ b/src/Features/Core/Portable/DesignerAttributes/AbstractDesignerAttributeService.cs @@ -115,7 +115,7 @@ internal static async Task Date: Tue, 2 May 2017 20:31:22 -0700 Subject: [PATCH 102/214] Simplify how we batch write to our persistence service. --- .../SQLitePersistentStorage.Accessor.cs | 11 +- .../SQLitePersistentStorage_WriteBatching.cs | 118 ++++++------------ 2 files changed, 47 insertions(+), 82 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs index 5317debdd0f95..7a4d33d740d2d 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage.Accessor.cs @@ -30,11 +30,12 @@ private abstract class Accessor new MultiDictionary>(); /// - /// Keep track of how many threads are trying to write out this particular queue. All threads - /// trying to write out the queue will wait until all the writes are done. + /// The task responsible for writing out all the batched actions we have for a particular + /// queue. When new reads come in for that queue they can 'await' this write-task completing + /// so that all reads for the queue observe any previously completed writes. /// - private readonly Dictionary _writeQueueKeyToCountdown = - new Dictionary(); + private readonly Dictionary _writeQueueKeyToWriteTask = + new Dictionary(); public Accessor(SQLitePersistentStorage storage) { @@ -119,7 +120,7 @@ await AddWriteTaskAsync(key, con => private Task FlushPendingWritesAsync(SqlConnection connection, TKey key, CancellationToken cancellationToken) => Storage.FlushSpecificWritesAsync( - connection, _writeQueueKeyToWrites, _writeQueueKeyToCountdown, GetWriteQueueKey(key), cancellationToken); + connection, _writeQueueKeyToWrites, _writeQueueKeyToWriteTask, GetWriteQueueKey(key), cancellationToken); private Task AddWriteTaskAsync(TKey key, Action action, CancellationToken cancellationToken) => Storage.AddWriteTaskAsync(_writeQueueKeyToWrites, GetWriteQueueKey(key), action, cancellationToken); diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index 3ffc4428f5a31..bab6cc762c88a 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -2,7 +2,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.SQLite.Interop; @@ -52,14 +51,14 @@ private async Task AddWriteTaskAsync( private async Task FlushSpecificWritesAsync( SqlConnection connection, MultiDictionary> keyToWriteActions, - Dictionary keyToCountdown, + Dictionary keyToWriteTask, TKey key, CancellationToken cancellationToken) { var writesToProcess = ArrayBuilder>.GetInstance(); try { await FlushSpecificWritesAsync( - connection, keyToWriteActions, keyToCountdown, key, writesToProcess, cancellationToken).ConfigureAwait(false); + connection, keyToWriteActions, keyToWriteTask, key, writesToProcess, cancellationToken).ConfigureAwait(false); } finally { @@ -69,19 +68,28 @@ await FlushSpecificWritesAsync( private async Task FlushSpecificWritesAsync( SqlConnection connection, MultiDictionary> keyToWriteActions, - Dictionary keyToCountdown, TKey key, + Dictionary keyToWriteTask, TKey key, ArrayBuilder> writesToProcess, CancellationToken cancellationToken) { - // Many threads many be trying to flush a specific queue. If some other thread - // beats us to writing this queue, we want to still wait until it is down. To - // accomplish that, we use a countdown that effectively states how many current - // writers there are, and which only lets us past once all the concurrent writers - // say they are done. - CountdownEvent countdown; - - // Note: by blocking on _writeQueueGate we are guaranteed to see all the writes - // performed by FlushAllPendingWrites. + // Get the task that is responsible for doing the writes for this queue. + // This task will complete when all previously enqueued writes for this queue + // complete, and all the currently enqueued writes for this queue complete as well. + var writeTask = await GetWriteTask( + connection, keyToWriteActions, keyToWriteTask, + key, writesToProcess, cancellationToken).ConfigureAwait(false); + + await writeTask.ConfigureAwait(false); + } + + private async Task GetWriteTask( + SqlConnection connection, MultiDictionary> keyToWriteActions, + Dictionary keyToWriteTask, TKey key, + ArrayBuilder> writesToProcess, + CancellationToken cancellationToken) + { + // Have to acqure the semaphore. We're going to mutate the shared 'keyToWriteActions' and + // 'keyToWriteTask' collections. using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { // Get the writes we need to process. @@ -90,76 +98,32 @@ private async Task FlushSpecificWritesAsync( // and clear them from the queues so we don't process things multiple times. keyToWriteActions.Remove(key); - // We may have acquired _writeQueueGate between the time that an existing thread - // completes the "Wait" below and grabs this lock. If that's the case, let go - // of the countdown associated with this key as it is no longer usable. - RemoveCountdownIfComplete(keyToCountdown, key); + // Find the existing task responsible for writing to this queue. + var existingWriteTask = keyToWriteTask.TryGetValue(key, out var task) + ? task + : SpecializedTasks.EmptyTask; - // See if there's an existing countdown keeping track of the number of writers - // writing this queue. - if (!keyToCountdown.TryGetValue(key, out countdown)) + if (writesToProcess.Count == 0) { - // We're the first writer for this queue. Set the count to one, and keep - // it around so future concurrent writers will see it. - countdown = new CountdownEvent(initialCount: 1); - keyToCountdown.Add(key, countdown); + // We have no writes of our own. But there may be an existing task that + // is writing out this queue. Return this so our caller can wait for + // all existing writes to complete. + return existingWriteTask; } - else - { - // If there is, increment the count to indicate that we're writing as well. - countdown.AddCount(); - } - - Debug.Assert(countdown.CurrentCount >= 1); - } - - // Now actually process any writes we found for this queue. - ProcessWriteQueue(connection, writesToProcess); - - // Mark that we're done writing out this queue, and wait until all other writers - // for this queue are done. Note: this needs to happen in the lock so that - // changes to the countdown value are observed consistently across all threads. - bool lastSignal; - using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - { - lastSignal = countdown.Signal(); - } - // Don't proceed until all concurrent writers of this queue complete. - countdown.Wait(); - - // If we're the thread that finally got the countdown to zero, then dispose of this - // count down and remove it from the dictionary (if it hasn't already been replaced - // by the next request). - if (lastSignal) - { - Debug.Assert(countdown.CurrentCount == 0); + // We have our own writes to process. Enqueue the task to write + // these out after the existing write-task for this queue completes. + var nextTask = existingWriteTask.ContinueWith( + _ => ProcessWriteQueue(connection, writesToProcess), + cancellationToken, + TaskContinuationOptions.RunContinuationsAsynchronously, + TaskScheduler.Default); - // Safe to call outside of lock. Countdown is only given out to a set of threads - // that have incremented it. And we can only get here once all the threads have - // been allowed to get past the 'Wait' point. Only one of those threads will - // have lastSignal set to true, so we'll only dispose this once. - countdown.Dispose(); + // Store this for the next flush call to see. + keyToWriteTask[key] = nextTask; - using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) - { - // Remove the countdown if it's still in the dictionary. It may not be if - // another thread came in after this batch of threads completed, and it - // removed the completed countdown already. - RemoveCountdownIfComplete(keyToCountdown, key); - } - } - } - - private void RemoveCountdownIfComplete( - Dictionary keyToCountdown, TKey key) - { - Debug.Assert(_writeQueueGate.CurrentCount == 0); - - if (keyToCountdown.TryGetValue(key, out var tempCountDown) && - tempCountDown.CurrentCount == 0) - { - keyToCountdown.Remove(key); + // And return this to our caller so it can 'await' all these writes completing. + return nextTask; } } From 6a46022b2324ca9a3154a7ea57fd18a735d2081b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 20:41:10 -0700 Subject: [PATCH 103/214] use a local function. --- .../SQLitePersistentStorage_WriteBatching.cs | 78 +++++++++---------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index bab6cc762c88a..6ed25b6f906c8 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -68,62 +68,58 @@ await FlushSpecificWritesAsync( private async Task FlushSpecificWritesAsync( SqlConnection connection, MultiDictionary> keyToWriteActions, - Dictionary keyToWriteTask, TKey key, + Dictionary keyToWriteTask, TKey key, ArrayBuilder> writesToProcess, CancellationToken cancellationToken) { // Get the task that is responsible for doing the writes for this queue. // This task will complete when all previously enqueued writes for this queue // complete, and all the currently enqueued writes for this queue complete as well. - var writeTask = await GetWriteTask( - connection, keyToWriteActions, keyToWriteTask, - key, writesToProcess, cancellationToken).ConfigureAwait(false); - + var writeTask = await GetWriteTask().ConfigureAwait(false); await writeTask.ConfigureAwait(false); - } - private async Task GetWriteTask( - SqlConnection connection, MultiDictionary> keyToWriteActions, - Dictionary keyToWriteTask, TKey key, - ArrayBuilder> writesToProcess, - CancellationToken cancellationToken) - { - // Have to acqure the semaphore. We're going to mutate the shared 'keyToWriteActions' and - // 'keyToWriteTask' collections. - using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + return; + + // Local functions + async Task GetWriteTask() { - // Get the writes we need to process. - writesToProcess.AddRange(keyToWriteActions[key]); + // Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions' + // and 'keyToWriteTask' collections. + using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) + { + // Get the writes we need to process. + writesToProcess.AddRange(keyToWriteActions[key]); - // and clear them from the queues so we don't process things multiple times. - keyToWriteActions.Remove(key); + // and clear them from the queues so we don't process things multiple times. + keyToWriteActions.Remove(key); - // Find the existing task responsible for writing to this queue. - var existingWriteTask = keyToWriteTask.TryGetValue(key, out var task) - ? task - : SpecializedTasks.EmptyTask; + // Find the existing task responsible for writing to this queue. + var existingWriteTask = keyToWriteTask.TryGetValue(key, out var task) + ? task + : SpecializedTasks.EmptyTask; - if (writesToProcess.Count == 0) - { - // We have no writes of our own. But there may be an existing task that - // is writing out this queue. Return this so our caller can wait for - // all existing writes to complete. - return existingWriteTask; - } + if (writesToProcess.Count == 0) + { + // We have no writes of our own. But there may be an existing task that + // is writing out this queue. Return this so our caller can wait for + // all existing writes to complete. + return existingWriteTask; + } - // We have our own writes to process. Enqueue the task to write - // these out after the existing write-task for this queue completes. - var nextTask = existingWriteTask.ContinueWith( - _ => ProcessWriteQueue(connection, writesToProcess), - cancellationToken, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default); + // We have our own writes to process. Enqueue the task to write + // these out after the existing write-task for this queue completes. + var nextTask = existingWriteTask.ContinueWith( + _ => ProcessWriteQueue(connection, writesToProcess), + cancellationToken, + TaskContinuationOptions.RunContinuationsAsynchronously, + TaskScheduler.Default); - // Store this for the next flush call to see. - keyToWriteTask[key] = nextTask; + // Store this for the next flush call to see. + keyToWriteTask[key] = nextTask; - // And return this to our caller so it can 'await' all these writes completing. - return nextTask; + // And return this to our caller so it can 'await' all these writes completing. + return nextTask; + } } } From 73dad6c34cf555ca90e03d3b46487960e56cd09d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 21:01:36 -0700 Subject: [PATCH 104/214] Add comment --- .../Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index 6ed25b6f906c8..d0d429f28ebae 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -108,6 +108,10 @@ async Task GetWriteTask() // We have our own writes to process. Enqueue the task to write // these out after the existing write-task for this queue completes. + // + // We're currently under a lock, so tell the continuation to run + // *asynchronously* so that the TPL does not try to execute it inline + // with this thread. var nextTask = existingWriteTask.ContinueWith( _ => ProcessWriteQueue(connection, writesToProcess), cancellationToken, From 79dfffa6848a02cb15a0571f60405677152390aa Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 21:02:33 -0700 Subject: [PATCH 105/214] Rename local function. --- .../Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index d0d429f28ebae..867448b9904e9 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -75,13 +75,13 @@ private async Task FlushSpecificWritesAsync( // Get the task that is responsible for doing the writes for this queue. // This task will complete when all previously enqueued writes for this queue // complete, and all the currently enqueued writes for this queue complete as well. - var writeTask = await GetWriteTask().ConfigureAwait(false); + var writeTask = await GetWriteTaskAsync().ConfigureAwait(false); await writeTask.ConfigureAwait(false); return; // Local functions - async Task GetWriteTask() + async Task GetWriteTaskAsync() { // Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions' // and 'keyToWriteTask' collections. From 0c9251aefa8519d92c53df1902c2bad441343e17 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 21:12:44 -0700 Subject: [PATCH 106/214] Actual flushing of data is non-cancellable. --- .../SQLite/SQLitePersistentStorage_WriteBatching.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index 867448b9904e9..578a1784cba17 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -112,9 +112,13 @@ async Task GetWriteTaskAsync() // We're currently under a lock, so tell the continuation to run // *asynchronously* so that the TPL does not try to execute it inline // with this thread. + // + // Note: this flushing is not cancellable. We've already removed the + // writes from the write queue. If we were not to write them out we + // would be losing data. var nextTask = existingWriteTask.ContinueWith( _ => ProcessWriteQueue(connection, writesToProcess), - cancellationToken, + CancellationToken.None, TaskContinuationOptions.RunContinuationsAsynchronously, TaskScheduler.Default); From 391d7e8089bb4696b77839479f17a995cfe1801a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 22:36:25 -0700 Subject: [PATCH 107/214] Update comment. --- .../Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index 578a1784cba17..d779c0d499ffb 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -85,6 +85,9 @@ async Task GetWriteTaskAsync() { // Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions' // and 'keyToWriteTask' collections. + // + // Note: by blocking on _writeQueueGate we are guaranteed to see all the writes + // performed by FlushAllPendingWritesAsync. using (await _writeQueueGate.DisposableWaitAsync(cancellationToken).ConfigureAwait(false)) { // Get the writes we need to process. From 1152ab73cf528abfbd86882de7ccd6524c1dd3f2 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Wed, 3 May 2017 09:26:33 -0700 Subject: [PATCH 108/214] Fix a unit test failure --- .../IOperation/IOperationTests_IParameterReferenceExpression.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs index c891508048a42..7b40d61f1950c 100644 --- a/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/Semantic/IOperation/IOperationTests_IParameterReferenceExpression.cs @@ -69,7 +69,7 @@ public void M(Point point) Children(2): IOperation: (OperationKind.None) (Syntax: 'var (x, y)') Children(2): ILocalReferenceExpression: x (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'x') ILocalReferenceExpression: y (OperationKind.LocalReferenceExpression, Type: System.Int32) (Syntax: 'y') - IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32, System.Int32)) (Syntax: 'point') + IConversionExpression (ConversionKind.Invalid, Implicit) (OperationKind.ConversionExpression, Type: (System.Int32 x, System.Int32 y)) (Syntax: 'point') IParameterReferenceExpression: point (OperationKind.ParameterReferenceExpression, Type: Point) (Syntax: 'point') "; var expectedDiagnostics = DiagnosticDescription.None; From abfca044653de618cda2bfd9b118b0421499e1ce Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Wed, 3 May 2017 10:56:23 -0700 Subject: [PATCH 109/214] Don't run naming rules on symbols with no name Fixes #19106 --- .../NamingStyles/NamingStylesTests.cs | 13 ++++++ .../NamingStylesTests_OptionSets.cs | 43 +++++++++++++++++-- .../NamingStyleDiagnosticAnalyzerBase.cs | 5 +++ 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs index bd8d373b97b57..9ec9e68a01925 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests.cs @@ -306,5 +306,18 @@ class D : C internal override void [|m|]() { } }", new TestParameters(options: MethodNamesArePascalCase)); } + + [Fact, Trait(Traits.Feature, Traits.Features.NamingStyle)] + [WorkItem(19106, "https://github.com/dotnet/roslyn/issues/19106")] + public async Task TestMissingOnSymbolsWithNoName() + { + await TestMissingInRegularAndScriptAsync( +@" +namespace Microsoft.CodeAnalysis.Host +{ + internal interface +[|}|] +", new TestParameters(options: InterfaceNamesStartWithI)); + } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests_OptionSets.cs b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests_OptionSets.cs index c28a080ee3c88..8ccb9885166c1 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests_OptionSets.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/NamingStyles/NamingStylesTests_OptionSets.cs @@ -21,9 +21,12 @@ public partial class NamingStylesTests : AbstractCSharpDiagnosticProviderBasedUs private IDictionary ParameterNamesAreCamelCase => Options(new OptionKey(SimplificationOptions.NamingPreferences, LanguageNames.CSharp), ParameterNamesAreCamelCaseOption()); - private IDictionary PropertyNamesArePascalCase => + private IDictionary PropertyNamesArePascalCase => Options(new OptionKey(SimplificationOptions.NamingPreferences, LanguageNames.CSharp), PropertyNamesArePascalCaseOption()); + private IDictionary InterfaceNamesStartWithI => + Options(new OptionKey(SimplificationOptions.NamingPreferences, LanguageNames.CSharp), InterfacesNamesStartWithIOption()); + private IDictionary Options(OptionKey option, object value) { var options = new Dictionary @@ -117,14 +120,14 @@ private NamingStylePreferences ParameterNamesAreCamelCaseOption() NamingStyleID = namingStyle.ID, EnforcementLevel = DiagnosticSeverity.Error }; - - var info = new NamingStylePreferences( + + var info = new NamingStylePreferences( ImmutableArray.Create(symbolSpecification), ImmutableArray.Create(namingStyle), ImmutableArray.Create(namingRule)); return info; - } + } private NamingStylePreferences PropertyNamesArePascalCaseOption() { @@ -157,5 +160,37 @@ private NamingStylePreferences PropertyNamesArePascalCaseOption() return info; } + + private NamingStylePreferences InterfacesNamesStartWithIOption() + { + var symbolSpecification = new SymbolSpecification( + null, + "Name", + ImmutableArray.Create(new SymbolSpecification.SymbolKindOrTypeKind(TypeKind.Interface)), + ImmutableArray.Empty, + ImmutableArray.Empty); + + var namingStyle = new NamingStyle( + Guid.NewGuid(), + capitalizationScheme: Capitalization.PascalCase, + name: "Name", + prefix: "I", + suffix: "", + wordSeparator: ""); + + var namingRule = new SerializableNamingRule() + { + SymbolSpecificationID = symbolSpecification.ID, + NamingStyleID = namingStyle.ID, + EnforcementLevel = DiagnosticSeverity.Error + }; + + var info = new NamingStylePreferences( + ImmutableArray.Create(symbolSpecification), + ImmutableArray.Create(namingStyle), + ImmutableArray.Create(namingRule)); + + return info; + } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/Diagnostics/Analyzers/NamingStyleDiagnosticAnalyzerBase.cs b/src/Features/Core/Portable/Diagnostics/Analyzers/NamingStyleDiagnosticAnalyzerBase.cs index 5998cef0c491d..ad196915dd108 100644 --- a/src/Features/Core/Portable/Diagnostics/Analyzers/NamingStyleDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Portable/Diagnostics/Analyzers/NamingStyleDiagnosticAnalyzerBase.cs @@ -56,6 +56,11 @@ private void SymbolAction( SymbolAnalysisContext context, ConcurrentDictionary> idToCachedResult) { + if (string.IsNullOrEmpty(context.Symbol.Name)) + { + return; + } + var namingPreferences = context.GetNamingStylePreferencesAsync().GetAwaiter().GetResult(); if (namingPreferences == null) { From 4e3db2b7a0732d45a720e9ed00c00cd22ab67a14 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Wed, 3 May 2017 10:59:57 -0700 Subject: [PATCH 110/214] Add comments Add comments explaining why we use the canonical name and "regular" name to map hierarchies from Solution Explorer to Roslyn projects. --- .../HierarchyItemToProjectIdMap.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs b/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs index 0e53ee3b146f5..46c6e7688b7d1 100644 --- a/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs +++ b/src/VisualStudio/Core/SolutionExplorerShim/HierarchyItemToProjectIdMap.cs @@ -29,6 +29,11 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo return false; } + // A project node is represented in two different hierarchies: the solution's IVsHierarchy (where it is a leaf node) + // and the project's own IVsHierarchy (where it is the root node). The IVsHierarchyItem joins them together for the + // purpose of creating the tree displayed in Solution Explorer. The project's hierarchy is what is passed from the + // project system to the language service, so that's the one the one to query here. To do that we need to get + // the "nested" hierarchy from the IVsHierarchyItem. var nestedHierarchy = hierarchyItem.HierarchyIdentity.NestedHierarchy; var nestedHierarchyId = hierarchyItem.HierarchyIdentity.NestedItemID; @@ -42,6 +47,12 @@ public bool TryGetProjectId(IVsHierarchyItem hierarchyItem, string targetFramewo var project = _workspace.DeferredState.ProjectTracker.ImmutableProjects .Where(p => { + // Here we try to match the hierarchy from Solution Explorer to a hierarchy from the Roslyn project. + // The canonical name of a hierarchy item must be unique _within_ an hierarchy, but since we're + // examining multiple hierarchies the canonical name could be the same. Indeed this happens when two + // project files are in the same folder--they both use the full path to the _folder_ as the canonical + // name. To distinguish them we also examine the "regular" name, which will necessarily be different + // if the two projects are in the same folder. if (p.Hierarchy.TryGetCanonicalName((uint)VSConstants.VSITEMID.Root, out string projectCanonicalName) && p.Hierarchy.TryGetItemName((uint)VSConstants.VSITEMID.Root, out string projectName) && projectCanonicalName.Equals(nestedCanonicalName, System.StringComparison.OrdinalIgnoreCase) From 922e7df0e886d2ed86cc47e65ef375e35464f0bd Mon Sep 17 00:00:00 2001 From: Charlie Powell Date: Wed, 3 May 2017 11:08:55 -0700 Subject: [PATCH 111/214] Fix example rulset XML (#18567) The example ruleset provided contains some syntax errors, so copy/pasting wholesale somewhere can result in build errors when including it. There was also a minor typo I found later in the file while reading through. --- docs/compilers/Rule Set Format.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/compilers/Rule Set Format.md b/docs/compilers/Rule Set Format.md index 48c5aa1a0e0b9..124d27be74464 100644 --- a/docs/compilers/Rule Set Format.md +++ b/docs/compilers/Rule Set Format.md @@ -11,7 +11,7 @@ Sample The following demonstrates a small but complete example of a .ruleset file. ``` XML - + @@ -20,12 +20,12 @@ The following demonstrates a small but complete example of a .ruleset file. - + - + ``` @@ -52,7 +52,7 @@ Within MSBuild project files the rule set can be specified via the `CodeAnalysis ``` -Note that because the rule set is specific via a *property* rather than an *item*, IDEs like Visual Studio will not show the rule set as a file in your project by default. For this reason it is common to explicitly include the file as an item as well: +Note that because the rule set is specified via a *property* rather than an *item*, IDEs like Visual Studio will not show the rule set as a file in your project by default. For this reason it is common to explicitly include the file as an item as well: ``` XML From 591038c83edcb6c4e479360e38dce7d5bdebe679 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 11:17:50 -0700 Subject: [PATCH 112/214] Fix test. --- .../Core/Test.Next/Services/ServiceHubServicesTests.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs index 3cea940b893b3..ca558d84ed835 100644 --- a/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs +++ b/src/VisualStudio/Core/Test.Next/Services/ServiceHubServicesTests.cs @@ -92,11 +92,11 @@ class Test { }"; var solution = workspace.CurrentSolution; - var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync( + var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync( solution, nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), - solution.Projects.First().DocumentIds.First(), CancellationToken.None); + solution.Projects.First().Id, CancellationToken.None); - Assert.Equal(result.DesignerAttributeArgument, "Form"); + Assert.Equal(result[0].DesignerAttributeArgument, "Form"); } } From 7202e48fa6f73a74ef261b04f5e882ae9235666e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 11:26:04 -0700 Subject: [PATCH 113/214] Actually keep track of the task we've kicked off to flush all writes to disk. --- .../Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index d779c0d499ffb..f2e3633283c5b 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -37,7 +37,7 @@ private async Task AddWriteTaskAsync( if (_flushAllTask == null) { var token = _shutdownTokenSource.Token; - var delay = + _flushAllTask = Task.Delay(FlushAllDelayMS, token) .ContinueWith( async _ => await FlushAllPendingWritesAsync(token).ConfigureAwait(false), From 5d8f15e026c73e3972c3e0b437a400e3a945de2d Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 3 May 2017 08:25:48 -0700 Subject: [PATCH 114/214] Fix the TestVsiNetCore runs This moves the logic for testVsiNetCore into the BuildAndTest.proj file. This matches the logic behavior for all of our other test runs. It also avoids us further complicating the necessary command line for calling the BuildAndTest.proj file. Eventually though want to move all of the test logic into a Powershell file. The MSBuild file at this point is approaching the complexity of a full script but lacking the flexibility. Should just convert it to a script and move on. --- BuildAndTest.proj | 2 ++ build/scripts/cibuild.ps1 | 10 +++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/BuildAndTest.proj b/BuildAndTest.proj index 385739580af0a..a2885ced06b47 100644 --- a/BuildAndTest.proj +++ b/BuildAndTest.proj @@ -7,10 +7,12 @@ $(MSBuildThisFileDirectory)Roslyn.sln $(MSBuildThisFileDirectory)src\Samples\Samples.sln + true Debug $(RunTestArgs) -xml $(RunTestArgs) -test64 $(RunTestArgs) -testVsi + $(RunTestArgs) -trait:Feature=NetCore $(RunTestArgs) -trait:$(Trait) $(RunTestArgs) -notrait:$(NoTrait) *.UnitTests.dll diff --git a/build/scripts/cibuild.ps1 b/build/scripts/cibuild.ps1 index 899e5995e3f14..8cee47e7b663e 100644 --- a/build/scripts/cibuild.ps1 +++ b/build/scripts/cibuild.ps1 @@ -159,15 +159,15 @@ try { $test64Arg = if ($test64 -and (-not $test32)) { "true" } else { "false" } $testVsiArg = if ($testVsi) { "true" } else { "false" } + $testVsiNetCoreArg = if ($testVsiNetCore) } { "true" } else { "false" } $buildLog = Join-Path $binariesdir "Build.log" - if ($testVsiNetCore) { - Run-MSBuild /p:BootstrapBuildPath="$bootstrapDir" BuildAndTest.proj /p:Configuration=$buildConfiguration /p:Test64=$test64Arg /p:TestVsi=true /p:Trait="Feature=NetCore" /p:PathMap="$($repoDir)=q:\roslyn" /p:Feature=pdb-path-determinism /fileloggerparameters:LogFile="$buildLog"`;verbosity=diagnostic /p:DeployExtension=false /p:RoslynRuntimeIdentifier=win7-x64 - } - else { - Run-MSBuild /p:BootstrapBuildPath="$bootstrapDir" BuildAndTest.proj /p:Configuration=$buildConfiguration /p:Test64=$test64Arg /p:TestVsi=$testVsiArg /p:TestDesktop=$testDesktop /p:TestCoreClr=$testCoreClr /p:PathMap="$($repoDir)=q:\roslyn" /p:Feature=pdb-path-determinism /fileloggerparameters:LogFile="$buildLog"`;verbosity=diagnostic /p:DeployExtension=false /p:RoslynRuntimeIdentifier=win7-x64 + if ($testVsiNetCore -and ($test32 -or $test64 -or $testVsi)) { + Write-Host "The testVsiNetCore option can't be combined with other test arguments" } + Run-MSBuild /p:BootstrapBuildPath="$bootstrapDir" BuildAndTest.proj /p:Configuration=$buildConfiguration /p:Test64=$test64Arg /p:TestVsi=$testVsiArg /p:TestDesktop=$testDesktop /p:TestCoreClr=$testCoreClr /p:TestVsiNetCore=$testVsiNetCoreArg /p:PathMap="$($repoDir)=q:\roslyn" /p:Feature=pdb-path-determinism /fileloggerparameters:LogFile="$buildLog"`;verbosity=diagnostic /p:DeployExtension=false /p:RoslynRuntimeIdentifier=win7-x64 + exit 0 } catch { From 144eaedabb435c67776499d5fc3d3225790b12e0 Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 3 May 2017 08:28:42 -0700 Subject: [PATCH 115/214] Fix up some test options Our Test.cmd script and Microbuild test running got messed up during the recent SDK change. A series of conflicts were just merged incorrectly. --- build/scripts/build.ps1 | 2 +- src/Tools/MicroBuild/Build.proj | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/scripts/build.ps1 b/build/scripts/build.ps1 index 0c53de2ab25be..1cebf68b6ee36 100644 --- a/build/scripts/build.ps1 +++ b/build/scripts/build.ps1 @@ -40,7 +40,7 @@ function Run-Build() { function Run-Test() { $proj = Join-Path $repoDir "BuildAndTest.proj" - $args = "/v:m /p:SkipCoreClr=true /p:ManualTest=true /t:Test $proj" + $args = "/v:m /p:SkipCoreClr=true /p:ManualTest=true /t:Test /p:Test32=true $proj" if ($test64) { $args += " /p:Test64=true" } diff --git a/src/Tools/MicroBuild/Build.proj b/src/Tools/MicroBuild/Build.proj index cd75fcdba0d3e..15a63f55bf426 100644 --- a/src/Tools/MicroBuild/Build.proj +++ b/src/Tools/MicroBuild/Build.proj @@ -38,7 +38,7 @@ - + From 64431269c18a0ae1417c53d6f85cb2e19e19cd2d Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Wed, 3 May 2017 08:48:30 -0700 Subject: [PATCH 116/214] Restore ability to deploy VSIX as a part of build The VS SDK team is interested in tracking down the deployment errors that we are seeing when deploying as a part of build. This change adds an option to make it easy to excercise that code path in builds. Going to use this option to quickly toggle to the build based deployment in order to mass test their fixes. --- BuildAndTest.proj | 16 ++++------------ build/Targets/Settings.props | 1 + 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/BuildAndTest.proj b/BuildAndTest.proj index a2885ced06b47..2a011fe2f8f68 100644 --- a/BuildAndTest.proj +++ b/BuildAndTest.proj @@ -15,51 +15,43 @@ $(RunTestArgs) -trait:Feature=NetCore $(RunTestArgs) -trait:$(Trait) $(RunTestArgs) -notrait:$(NoTrait) + false *.UnitTests.dll *.IntegrationTests.dll $(MSBuildThisFileDirectory)Binaries\$(Configuration)\ $(RunTestArgs) -log:"$(OutputDirectory)\runtests.log" $(OutputDirectory)\CoreClrTest - - RestorePackages=false; - TreatWarningsAsErrors=true; - DeployExtension=false; - @@ -117,7 +109,7 @@ - + $(NuGetPackageRoot)\roslyntools.microsoft.vsixexpinstaller\$(RoslynToolsMicrosoftVSIXExpInstallerVersion)\tools\VsixExpInstaller.exe diff --git a/build/Targets/Settings.props b/build/Targets/Settings.props index f0933dfe67e3a..11f8ecd249bf1 100644 --- a/build/Targets/Settings.props +++ b/build/Targets/Settings.props @@ -45,6 +45,7 @@ $(VisualStudioReferenceMajorVersion).0.0.0 Dev$(VisualStudioReferenceMajorVersion) $(VisualStudioVersion) + true diff --git a/build/scripts/build.ps1 b/build/scripts/build.ps1 index 1cebf68b6ee36..e8a21ed177ed6 100644 --- a/build/scripts/build.ps1 +++ b/build/scripts/build.ps1 @@ -40,7 +40,7 @@ function Run-Build() { function Run-Test() { $proj = Join-Path $repoDir "BuildAndTest.proj" - $args = "/v:m /p:SkipCoreClr=true /p:ManualTest=true /t:Test /p:Test32=true $proj" + $args = "/v:m /p:ManualTest=true /t:Test /p:TestDesktop=true $proj" if ($test64) { $args += " /p:Test64=true" } From abcabd072624f3728ead6017ab559e9d295502ab Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 15:22:00 -0700 Subject: [PATCH 130/214] Do not allow 'add nuget reference' for projects that we fail calling into nuget over. --- ...olReferenceFinder_PackageAssemblySearch.cs | 2 +- .../AbstractAddPackageCodeFixProvider.cs | 2 +- .../DesignerAttributeIncrementalAnalyzer.cs | 2 - .../PackageInstallerService.ProjectState.cs | 21 +++++++ .../PackageInstallerServiceFactory.cs | 57 +++++++++++++------ .../Core/Def/ServicesVisualStudio.csproj | 1 + .../Packaging/IPackageInstallerService.cs | 1 + 7 files changed, 65 insertions(+), 21 deletions(-) create mode 100644 src/VisualStudio/Core/Def/Packaging/PackageInstallerService.ProjectState.cs diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index 9945ac8a2dfdc..dc0c4b2787515 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -84,7 +84,7 @@ await FindReferenceAssemblyTypeReferencesAsync( if (symbolSearchService != null && installerService != null && searchNugetPackages && - installerService.IsEnabled) + installerService.IsEnabledForProject(_document.Project.Id)) { foreach (var packageSource in installerService.PackageSources) { diff --git a/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs b/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs index 26178cc3e638a..e1cd2786efb40 100644 --- a/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs @@ -51,7 +51,7 @@ protected async Task> GetAddPackagesCodeActionsAsync( if (symbolSearchService != null && installerService != null && searchNugetPackages && - installerService.IsEnabled) + installerService.IsEnabledForProject(document.Project.Id)) { foreach (var packageSource in installerService.PackageSources) { diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index b62c4cf3fe5c8..43ee00a0a7c89 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs @@ -78,7 +78,6 @@ public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, In return; } -#if false // CPS projects do not support designer attributes. So we just skip these projects entirely. var isCPSProject = await Task.Factory.StartNew( () => vsWorkspace.IsCPSProject(project), @@ -90,7 +89,6 @@ public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, In { return; } -#endif // Try to compute this data in the remote process. If that fails, then compute // the results in the local process. diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerService.ProjectState.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerService.ProjectState.cs new file mode 100644 index 0000000000000..e4a1f6003d77f --- /dev/null +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerService.ProjectState.cs @@ -0,0 +1,21 @@ +// 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; + +namespace Microsoft.VisualStudio.LanguageServices.Packaging +{ + internal partial class PackageInstallerService + { + private struct ProjectState + { + public readonly bool IsEnabled; + public readonly Dictionary InstalledPackageToVersion; + + public ProjectState(bool isEnabled, Dictionary installedPackageToVersion) + { + IsEnabled = isEnabled; + InstalledPackageToVersion = installedPackageToVersion; + } + } + } +} diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index 51264ba221f47..b9bd9a3610542 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -56,8 +56,8 @@ internal partial class PackageInstallerService : AbstractDelayStartedService, IP private bool _solutionChanged; private HashSet _changedProjects = new HashSet(); - private readonly ConcurrentDictionary> _projectToInstalledPackageAndVersion = - new ConcurrentDictionary>(); + private readonly ConcurrentDictionary _projectToInstalledPackageAndVersion = + new ConcurrentDictionary(); [ImportingConstructor] public PackageInstallerService( @@ -79,6 +79,22 @@ public PackageInstallerService( public bool IsEnabled => _packageServices != null; + public bool IsEnabledForProject(ProjectId projectId) + { + if (!IsEnabled) + { + return false; + } + + if (_projectToInstalledPackageAndVersion.TryGetValue(projectId, out var state)) + { + return state.IsEnabled; + } + + // If we haven't scanned the project yet, assume that we're available for it. + return true; + } + protected override void EnableService() { // Our service has been enabled. Now load the VS package dlls. @@ -388,8 +404,9 @@ private ProjectId DequeueNextProject(Solution solution) private void ProcessProjectChange(Solution solution, ProjectId projectId) { this.AssertIsForeground(); + // Remove anything we have associated with this project. - _projectToInstalledPackageAndVersion.TryRemove(projectId, out var installedPackages); + _projectToInstalledPackageAndVersion.TryRemove(projectId, out var projectState); var project = solution.GetProject(projectId); if (project == null) @@ -416,26 +433,35 @@ private void ProcessProjectChange(Solution solution, ProjectId projectId) return; } - installedPackages = new Dictionary(); + var installedPackages = new Dictionary(); + var isEnabled = false; // Calling into NuGet. Assume they may fail for any reason. try { var installedPackageMetadata = _packageServices.GetInstalledPackages(dteProject); installedPackages.AddRange(installedPackageMetadata.Select(m => new KeyValuePair(m.Id, m.VersionString))); + isEnabled = true; + } + catch (ArgumentException) + { + // Nuget may throw an ArgumentException when there is something about the project + // they do not like/support. } catch (Exception e) when (FatalError.ReportWithoutCrash(e)) { } - _projectToInstalledPackageAndVersion.AddOrUpdate(projectId, installedPackages, (_1, _2) => installedPackages); + var state = new ProjectState(isEnabled, installedPackages); + _projectToInstalledPackageAndVersion.AddOrUpdate( + projectId, state, (_1, _2) => state); } public bool IsInstalled(Workspace workspace, ProjectId projectId, string packageName) { ThisCanBeCalledOnAnyThread(); return _projectToInstalledPackageAndVersion.TryGetValue(projectId, out var installedPackages) && - installedPackages.ContainsKey(packageName); + installedPackages.InstalledPackageToVersion.ContainsKey(packageName); } public ImmutableArray GetInstalledVersions(string packageName) @@ -443,10 +469,10 @@ public ImmutableArray GetInstalledVersions(string packageName) ThisCanBeCalledOnAnyThread(); var installedVersions = new HashSet(); - foreach (var installedPackages in _projectToInstalledPackageAndVersion.Values) + foreach (var state in _projectToInstalledPackageAndVersion.Values) { string version = null; - if (installedPackages?.TryGetValue(packageName, out version) == true && version != null) + if (state.InstalledPackageToVersion.TryGetValue(packageName, out version) && version != null) { installedVersions.Add(version); } @@ -494,16 +520,13 @@ public IEnumerable GetProjectsWithInstalledPackage(Solution solution, s foreach (var kvp in this._projectToInstalledPackageAndVersion) { - var installedPackageAndVersion = kvp.Value; - if (installedPackageAndVersion != null) + var state = kvp.Value; + if (state.InstalledPackageToVersion.TryGetValue(packageName, out var installedVersion) && installedVersion == version) { - if (installedPackageAndVersion.TryGetValue(packageName, out var installedVersion) && installedVersion == version) + var project = solution.GetProject(kvp.Key); + if (project != null) { - var project = solution.GetProject(kvp.Key); - if (project != null) - { - result.Add(project); - } + result.Add(project); } } } @@ -631,4 +654,4 @@ public void UninstallPackage(EnvDTE.Project project, string packageId, bool remo => _packageUninstaller.UninstallPackage(project, packageId, removeDependencies); } } -} +} \ No newline at end of file diff --git a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj index 7d8e9ac64eaac..1809a21b16135 100644 --- a/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj +++ b/src/VisualStudio/Core/Def/ServicesVisualStudio.csproj @@ -174,6 +174,7 @@ + diff --git a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs index 46b94400ed46d..2b69d32614dec 100644 --- a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs +++ b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs @@ -12,6 +12,7 @@ namespace Microsoft.CodeAnalysis.Packaging internal interface IPackageInstallerService : IWorkspaceService { bool IsEnabled { get; } + bool IsEnabledForProject(ProjectId projectId); bool IsInstalled(Workspace workspace, ProjectId projectId, string packageName); From cec98c43d23c8ad74d5124a89f4744a0d8d3878c Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 17:01:16 -0700 Subject: [PATCH 131/214] Update tests. --- .../CSharpTest/AddUsing/AddUsingTests_NuGet.cs | 14 +++++++------- .../Diagnostics/AddImport/AddImportTests_NuGet.vb | 14 +++++++------- .../SymbolReferenceFinder_PackageAssemblySearch.cs | 2 +- .../AbstractAddPackageCodeFixProvider.cs | 2 +- .../Packaging/PackageInstallerServiceFactory.cs | 6 +++--- .../Portable/Packaging/IPackageInstallerService.cs | 3 +-- 6 files changed, 20 insertions(+), 21 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_NuGet.cs b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_NuGet.cs index 53155eedcb87e..28f2e728a387f 100644 --- a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_NuGet.cs +++ b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests_NuGet.cs @@ -54,7 +54,7 @@ public async Task TestSearchPackageSingleName() // Make a loose mock for the installer service. We don't care what this test // calls on it. var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.TryInstallPackage(It.IsAny(), It.IsAny(), It.IsAny(), "NuGetPackage", It.IsAny(), It.IsAny(), It.IsAny())) .Returns(true); @@ -83,7 +83,7 @@ public async Task TestSearchPackageMultipleNames() // Make a loose mock for the installer service. We don't care what this test // calls on it. var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.TryInstallPackage(It.IsAny(), It.IsAny(), It.IsAny(), "NuGetPackage", It.IsAny(), It.IsAny(), It.IsAny())) .Returns(true); @@ -112,7 +112,7 @@ public async Task TestMissingIfPackageAlreadyInstalled() // Make a loose mock for the installer service. We don't care what this test // calls on it. var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.IsInstalled(It.IsAny(), It.IsAny(), "NuGetPackage")) .Returns(true); @@ -135,7 +135,7 @@ public async Task TestOptionsOffered() // Make a loose mock for the installer service. We don't care what this test // calls on it. var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.GetInstalledVersions("NuGetPackage")) .Returns(ImmutableArray.Create("1.0", "2.0")); @@ -177,7 +177,7 @@ await TestSmartTagTextAsync( public async Task TestInstallGetsCalledNoVersion() { var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.TryInstallPackage(It.IsAny(), It.IsAny(), It.IsAny(), "NuGetPackage", /*versionOpt*/ null, It.IsAny(), It.IsAny())) .Returns(true); @@ -205,7 +205,7 @@ class C public async Task TestInstallGetsCalledWithVersion() { var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.GetInstalledVersions("NuGetPackage")) .Returns(ImmutableArray.Create("1.0")); @@ -235,7 +235,7 @@ class C public async Task TestFailedInstallRollsBackFile() { var installerServiceMock = new Mock(MockBehavior.Loose); - installerServiceMock.SetupGet(i => i.IsEnabled).Returns(true); + installerServiceMock.Setup(i => i.IsEnabled(It.IsAny())).Returns(true); installerServiceMock.SetupGet(i => i.PackageSources).Returns(NugetPackageSources); installerServiceMock.Setup(s => s.GetInstalledVersions("NuGetPackage")) .Returns(ImmutableArray.Create("1.0")); diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb index 92d7484321008..fff18f70367c7 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests_NuGet.vb @@ -44,7 +44,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.AddImp ' Make a loose mock for the installer service. We don't care what this test ' calls on it. Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.TryInstallPackage(It.IsAny(Of Workspace), It.IsAny(Of DocumentId), It.IsAny(Of String), "NuGetPackage", It.IsAny(Of String), It.IsAny(Of Boolean), It.IsAny(Of CancellationToken))). Returns(True) @@ -71,7 +71,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa ' Make a loose mock for the installer service. We don't care what this test ' calls on it. Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.TryInstallPackage(It.IsAny(Of Workspace), It.IsAny(Of DocumentId), It.IsAny(Of String), "NuGetPackage", It.IsAny(Of String), It.IsAny(Of Boolean), It.IsAny(Of CancellationToken))). Returns(True) @@ -98,7 +98,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa ' Make a loose mock for the installer service. We don't care what this test ' calls on it. Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.TryInstallPackage(It.IsAny(Of Workspace), It.IsAny(Of DocumentId), It.IsAny(Of String), "NuGetPackage", It.IsAny(Of String), It.IsAny(Of Boolean), It.IsAny(Of CancellationToken))). Returns(False) @@ -123,7 +123,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa ' Make a loose mock for the installer service. We don't care what this test ' calls on it. Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.IsInstalled(It.IsAny(Of Workspace)(), It.IsAny(Of ProjectId)(), "NuGetPackage")). Returns(True) @@ -145,7 +145,7 @@ New TestParameters(fixProviderData:=New ProviderData(installerServiceMock.Object ' Make a loose mock for the installer service. We don't care what this test ' calls on it. Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.GetInstalledVersions("NuGetPackage")). Returns(ImmutableArray.Create("1.0", "2.0")) @@ -185,7 +185,7 @@ parameters:=New TestParameters(fixProviderData:=data)) Public Async Function TestInstallGetsCalledNoVersion() As Task Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.TryInstallPackage(It.IsAny(Of Workspace), It.IsAny(Of DocumentId), It.IsAny(Of String), "NuGetPackage", Nothing, It.IsAny(Of Boolean), It.IsAny(Of CancellationToken))). Returns(True) @@ -211,7 +211,7 @@ End Class", fixProviderData:=New ProviderData(installerServiceMock.Object, packa Public Async Function TestInstallGetsCalledWithVersion() As Task Dim installerServiceMock = New Mock(Of IPackageInstallerService)(MockBehavior.Loose) - installerServiceMock.SetupGet(Function(i) i.IsEnabled).Returns(True) + installerServiceMock.Setup(Function(i) i.IsEnabled(It.IsAny(Of ProjectId))).Returns(True) installerServiceMock.SetupGet(Function(i) i.PackageSources).Returns(NugetPackageSources) installerServiceMock.Setup(Function(s) s.GetInstalledVersions("NuGetPackage")). Returns(ImmutableArray.Create("1.0")) diff --git a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs index dc0c4b2787515..c3bce9a2bfa1d 100644 --- a/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs +++ b/src/Features/Core/Portable/AddImport/SymbolReferenceFinder_PackageAssemblySearch.cs @@ -84,7 +84,7 @@ await FindReferenceAssemblyTypeReferencesAsync( if (symbolSearchService != null && installerService != null && searchNugetPackages && - installerService.IsEnabledForProject(_document.Project.Id)) + installerService.IsEnabled(_document.Project.Id)) { foreach (var packageSource in installerService.PackageSources) { diff --git a/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs b/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs index e1cd2786efb40..7d5f063e534cd 100644 --- a/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs +++ b/src/Features/Core/Portable/AddPackage/AbstractAddPackageCodeFixProvider.cs @@ -51,7 +51,7 @@ protected async Task> GetAddPackagesCodeActionsAsync( if (symbolSearchService != null && installerService != null && searchNugetPackages && - installerService.IsEnabledForProject(document.Project.Id)) + installerService.IsEnabled(document.Project.Id)) { foreach (var packageSource in installerService.PackageSources) { diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index b9bd9a3610542..261e8c760b05f 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -77,11 +77,11 @@ public PackageInstallerService( public event EventHandler PackageSourcesChanged; - public bool IsEnabled => _packageServices != null; + private bool IsEnabled => _packageServices != null; - public bool IsEnabledForProject(ProjectId projectId) + bool IPackageInstallerService.IsEnabled(ProjectId projectId) { - if (!IsEnabled) + if (_packageServices == null) { return false; } diff --git a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs index 2b69d32614dec..40b136736450f 100644 --- a/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs +++ b/src/Workspaces/Core/Portable/Packaging/IPackageInstallerService.cs @@ -11,8 +11,7 @@ namespace Microsoft.CodeAnalysis.Packaging { internal interface IPackageInstallerService : IWorkspaceService { - bool IsEnabled { get; } - bool IsEnabledForProject(ProjectId projectId); + bool IsEnabled(ProjectId projectId); bool IsInstalled(Workspace workspace, ProjectId projectId, string packageName); From ee4eec9d35379e14d11eae2d763286fdf48bcdf6 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 18:22:43 -0700 Subject: [PATCH 132/214] Don't offer to add a null check for an unknown type when one already exists. --- .../AddParameterCheckTests.cs | 17 +++++++++++++++++ ...tAddParameterCheckCodeRefactoringProvider.cs | 7 +++++-- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs index 870815e7c612f..69bf6ab5533f1 100644 --- a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs @@ -704,5 +704,22 @@ public C(string s) } }", index: 2); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] + public async Task TestMissingOnUnboundTypeWithExistingNullCheck() + { + await TestMissingAsync( +@" +class C +{ + public C(String [||]s) + { + if (s == null) + { + throw new System.Exception(); + } + } +}"); + } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 1d5571df4144a..462ed31382c16 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -117,7 +117,10 @@ private bool IsIfNullCheck(IOperation statement, IParameterSymbol parameter) { if (statement is IIfStatement ifStatement) { - if (ifStatement.Condition is IBinaryOperatorExpression binaryOperator) + var condition = ifStatement.Condition; + condition = UnwrapImplicitConversion(condition); + + if (condition is IBinaryOperatorExpression binaryOperator) { // Look for code of the form "if (p == null)" or "if (null == p)" if (IsNullCheck(binaryOperator.LeftOperand, binaryOperator.RightOperand, parameter) || @@ -127,7 +130,7 @@ private bool IsIfNullCheck(IOperation statement, IParameterSymbol parameter) } } else if (parameter.Type.SpecialType == SpecialType.System_String && - IsStringCheck(ifStatement.Condition, parameter)) + IsStringCheck(condition, parameter)) { return true; } From 1ce46dd41023c5ae45ff7c9082b1af1ac5baa7fb Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 18:38:19 -0700 Subject: [PATCH 133/214] Respect user options around string/String when generating parameter check. --- .../AddParameterCheckTests.cs | 34 +++++++++++++++++++ ...ddParameterCheckCodeRefactoringProvider.cs | 2 -- ...ddParameterCheckCodeRefactoringProvider.cs | 4 ++- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs index 69bf6ab5533f1..b700377c7cebf 100644 --- a/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs +++ b/src/EditorFeatures/CSharpTest/InitializeParameter/AddParameterCheckTests.cs @@ -705,6 +705,7 @@ public C(string s) }", index: 2); } + [WorkItem(19173, "https://github.com/dotnet/roslyn/issues/19173")] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] public async Task TestMissingOnUnboundTypeWithExistingNullCheck() { @@ -721,5 +722,38 @@ public C(String [||]s) } }"); } + + [WorkItem(19174, "https://github.com/dotnet/roslyn/issues/19174")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsInitializeParameter)] + public async Task TestRespectPredefinedTypePreferences() + { + await TestInRegularAndScript1Async( +@" +using System; + +class Program +{ + static void Main([||]String bar) + { + } +}", +@" +using System; + +class Program +{ + static void Main(String bar) + { + if (String.IsNullOrEmpty(bar)) + { + throw new ArgumentException(""message"", nameof(bar)); + } + } +}", index: 1, + parameters: new TestParameters( + options: Option( + CodeStyleOptions.PreferIntrinsicPredefinedTypeKeywordInMemberAccess, + CodeStyleOptions.FalseWithSuggestionEnforcement))); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs b/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs index 55e0d1e65dac5..b495f04a52263 100644 --- a/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/CSharp/Portable/InitializeParameter/CSharpAddParameterCheckCodeRefactoringProvider.cs @@ -1,12 +1,10 @@ // 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.Composition; using Microsoft.CodeAnalysis.CodeRefactorings; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Editing; using Microsoft.CodeAnalysis.InitializeParameter; -using Microsoft.CodeAnalysis.Semantics; namespace Microsoft.CodeAnalysis.CSharp.InitializeParameter { diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 462ed31382c16..589e69cd37fbb 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -241,11 +241,13 @@ private static TStatementSyntax CreateStringCheckStatement( Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter, string methodName) { + var stringType = compilation.GetSpecialType(SpecialType.System_String); + // generates: if (string.IsXXX(s)) throw new ArgumentException("message", nameof(s)) return (TStatementSyntax)generator.IfStatement( generator.InvocationExpression( generator.MemberAccessExpression( - generator.TypeExpression(SpecialType.System_String), + generator.TypeExpression(stringType), generator.IdentifierName(methodName)), generator.Argument(generator.IdentifierName(parameter.Name))), SpecializedCollections.SingletonEnumerable( From e79ae96d870d25a0a1825e0d63fc6968aada6bf5 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 18:55:15 -0700 Subject: [PATCH 134/214] Be resilient to not being able to find ArgumentNullException --- ...stractAddParameterCheckCodeRefactoringProvider.cs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 589e69cd37fbb..3b8a609ce7edc 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -377,8 +377,18 @@ private async Task TryAddNullCheckToAssignmentAsync( private static SyntaxNode CreateArgumentNullException( Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter) { + var argumentNullExceptionType = compilation.GetTypeByMetadataName(typeof(ArgumentNullException).FullName); + if (argumentNullExceptionType == null) + { + return generator.ObjectCreationExpression( + generator.QualifiedName( + generator.IdentifierName(nameof(System)), + generator.IdentifierName(nameof(ArgumentNullException))), + generator.NameOfExpression(generator.IdentifierName(parameter.Name))); + } + return generator.ObjectCreationExpression( - compilation.GetTypeByMetadataName("System.ArgumentNullException"), + argumentNullExceptionType, generator.NameOfExpression(generator.IdentifierName(parameter.Name))); } From 6a26c852391e9359d23a2f1680023a730408700e Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 19:03:26 -0700 Subject: [PATCH 135/214] Be resilient to not being able to find ArgumentNullException --- ...ddParameterCheckCodeRefactoringProvider.cs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs index 3b8a609ce7edc..8f55faec5f59e 100644 --- a/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/InitializeParameter/AbstractAddParameterCheckCodeRefactoringProvider.cs @@ -374,21 +374,25 @@ private async Task TryAddNullCheckToAssignmentAsync( return null; } - private static SyntaxNode CreateArgumentNullException( - Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter) + private static SyntaxNode GetTypeNode( + Compilation compilation, SyntaxGenerator generator, Type type) { - var argumentNullExceptionType = compilation.GetTypeByMetadataName(typeof(ArgumentNullException).FullName); - if (argumentNullExceptionType == null) + var typeSymbol = compilation.GetTypeByMetadataName(type.FullName); + if (typeSymbol == null) { - return generator.ObjectCreationExpression( - generator.QualifiedName( - generator.IdentifierName(nameof(System)), - generator.IdentifierName(nameof(ArgumentNullException))), - generator.NameOfExpression(generator.IdentifierName(parameter.Name))); + return generator.QualifiedName( + generator.IdentifierName(nameof(System)), + generator.IdentifierName(type.Name)); } + return generator.TypeExpression(typeSymbol); + } + + private static SyntaxNode CreateArgumentNullException( + Compilation compilation, SyntaxGenerator generator, IParameterSymbol parameter) + { return generator.ObjectCreationExpression( - argumentNullExceptionType, + GetTypeNode(compilation, generator, typeof(ArgumentNullException)), generator.NameOfExpression(generator.IdentifierName(parameter.Name))); } @@ -398,7 +402,7 @@ private static SyntaxNode CreateArgumentException( // Note "message" is not localized. It is the name of the first parameter of // "ArgumentException" return generator.ObjectCreationExpression( - compilation.GetTypeByMetadataName("System.ArgumentException"), + GetTypeNode(compilation, generator, typeof(ArgumentException)), generator.LiteralExpression("message"), generator.NameOfExpression(generator.IdentifierName(parameter.Name))); } From 89de92564478e23ce2565e65c034bd96edada5a1 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 21:16:21 -0700 Subject: [PATCH 136/214] Add type inference support for tuples. --- .../GenerateMethod/GenerateMethodTests.cs | 64 ++++++++++++++++++- ...CSharpTypeInferenceService.TypeInferrer.cs | 17 +++++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs index 3532565a413de..5866588f098b2 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateMethod/GenerateMethodTests.cs @@ -7262,7 +7262,7 @@ private object Method() parseOptions: TestOptions.Regular); } - [Fact/*(Skip = "https://github.com/dotnet/roslyn/issues/15508")*/, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WorkItem(14136, "https://github.com/dotnet/roslyn/issues/14136")] public async Task TestDeconstruction4() { @@ -7379,5 +7379,67 @@ private ref int Bar() } }"); } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + [WorkItem(18969, "https://github.com/dotnet/roslyn/issues/18969")] + public async Task TestTupleElement1() + { + await TestAsync( +@"using System; + +class C +{ + public void M1() + { + (int x, string y) t = ([|Method|](), null); + } +}", +@"using System; + +class C +{ + public void M1() + { + (int x, string y) t = (Method(), null); + } + + private int Method() + { + throw new NotImplementedException(); + } +}", +parseOptions: TestOptions.Regular); + } + + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + [WorkItem(18969, "https://github.com/dotnet/roslyn/issues/18969")] + public async Task TestTupleElement2() + { + await TestAsync( +@"using System; + +class C +{ + public void M1() + { + (int x, string y) t = (0, [|Method|]()); + } +}", +@"using System; + +class C +{ + public void M1() + { + (int x, string y) t = (0, Method()); + } + + private string Method() + { + throw new NotImplementedException(); + } +}", +parseOptions: TestOptions.Regular); + } } } \ 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 04cdd88ad91b3..f6eaeaf734fa0 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -310,6 +310,11 @@ private IEnumerable InferTypeInArgument( return InferTypeInElementAccessExpression(elementAccess, index, argument); } + + if (argument.IsParentKind(SyntaxKind.TupleExpression)) + { + return InferTypeInTupleExpression((TupleExpressionSyntax)argument.Parent, argument); + } } if (argument.Parent.IsParentKind(SyntaxKind.ImplicitElementAccess) && @@ -331,6 +336,18 @@ private IEnumerable InferTypeInArgument( return SpecializedCollections.EmptyEnumerable(); } + private IEnumerable InferTypeInTupleExpression( + TupleExpressionSyntax parent, ArgumentSyntax argument) + { + var index = parent.Arguments.IndexOf(argument); + var parentTypes = InferTypes(parent); + + return parentTypes.Select(typeInfo => typeInfo.InferredType) + .OfType() + .Where(namedType => namedType.IsTupleType && index < namedType.TupleElements.Length) + .Select(tupleType => new TypeInferenceInfo(tupleType.TupleElements[index].Type)); + } + private IEnumerable InferTypeInAttributeArgument(AttributeArgumentSyntax argument, SyntaxToken? previousToken = null, ArgumentSyntax argumentOpt = null) { if (previousToken.HasValue) From 1c8f1af7963daee4290d49a43cf23cb6f5ae31d4 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Wed, 3 May 2017 21:34:01 -0700 Subject: [PATCH 137/214] Add VB support for inferring tuple element types. --- .../GenerateMethod/GenerateMethodTests.vb | 56 +++++++++++++++++++ ...CSharpTypeInferenceService.TypeInferrer.cs | 6 +- ...lBasicTypeInferenceService.TypeInferrer.vb | 35 +++++++++++- 3 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb index 3d589a34251cd..d2210b700b9b4 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateMethod/GenerateMethodTests.vb @@ -3926,6 +3926,62 @@ Class Program End Class") End Function + + + Public Async Function TupleElement1() As Task + Await TestInRegularAndScriptAsync( +" +Imports System + +Public Class Q + Sub Main() + Dim x As (Integer, String) = ([|Foo|](), """") + End Sub +End Class +", +" +Imports System + +Public Class Q + Sub Main() + Dim x As (Integer, String) = (Foo(), """") + End Sub + + Private Function Foo() As Integer + Throw New NotImplementedException() + End Function +End Class +") + End Function + + + + Public Async Function TupleElement2() As Task + Await TestInRegularAndScriptAsync( +" +Imports System + +Public Class Q + Sub Main() + Dim x As (Integer, String) = (0, [|Foo|]()) + End Sub +End Class +", +" +Imports System + +Public Class Q + Sub Main() + Dim x As (Integer, String) = (0, Foo()) + End Sub + + Private Function Foo() As String + Throw New NotImplementedException() + End Function +End Class +") + End Function + Public Async Function MethodWithTupleWithNames() As Task Await TestInRegularAndScriptAsync( diff --git a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs index f6eaeaf734fa0..8a9c40f5c2c61 100644 --- a/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs +++ b/src/Workspaces/CSharp/Portable/LanguageServices/CSharpTypeInferenceService.TypeInferrer.cs @@ -337,10 +337,10 @@ private IEnumerable InferTypeInArgument( } private IEnumerable InferTypeInTupleExpression( - TupleExpressionSyntax parent, ArgumentSyntax argument) + TupleExpressionSyntax tupleExpression, ArgumentSyntax argument) { - var index = parent.Arguments.IndexOf(argument); - var parentTypes = InferTypes(parent); + var index = tupleExpression.Arguments.IndexOf(argument); + var parentTypes = InferTypes(tupleExpression); return parentTypes.Select(typeInfo => typeInfo.InferredType) .OfType() diff --git a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicTypeInferenceService.TypeInferrer.vb b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicTypeInferenceService.TypeInferrer.vb index 9618d49a3d092..9edca4167d799 100644 --- a/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicTypeInferenceService.TypeInferrer.vb +++ b/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicTypeInferenceService.TypeInferrer.vb @@ -53,7 +53,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Return parent.TypeSwitch( Function(addRemoveHandlerStatement As AddRemoveHandlerStatementSyntax) InferTypeInAddRemoveHandlerStatementSyntax(addRemoveHandlerStatement, expression), - Function(argument As ArgumentSyntax) InferTypeInArgumentList(TryCast(argument.Parent, ArgumentListSyntax), argument), + Function(argument As ArgumentSyntax) InferTypeInArgument(argument), Function(arrayCreationExpression As ArrayCreationExpressionSyntax) InferTypeInArrayCreationExpression(arrayCreationExpression), Function(arrayRank As ArrayRankSpecifierSyntax) InferTypeInArrayRankSpecifier(), Function(arrayType As ArrayTypeSyntax) InferTypeInArrayType(arrayType), @@ -124,7 +124,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic Dim parent = token.Parent Return parent.TypeSwitch( - Function(argument As ArgumentSyntax) InferTypeInArgumentList(TryCast(argument.Parent, ArgumentListSyntax), previousToken:=token), + Function(argument As ArgumentSyntax) InferTypeInArgument(argument, previousToken:=token), Function(argumentList As ArgumentListSyntax) InferTypeInArgumentList(argumentList, previousToken:=token), Function(arrayCreationExpression As ArrayCreationExpressionSyntax) InferTypeInArrayCreationExpression(arrayCreationExpression), Function(arrayRank As ArrayRankSpecifierSyntax) InferTypeInArrayRankSpecifier(), @@ -171,13 +171,42 @@ Namespace Microsoft.CodeAnalysis.VisualBasic SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)()) End Function + Private Function InferTypeInArgument(argument As ArgumentSyntax, + Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo) + If TypeOf argument.Parent Is ArgumentListSyntax Then + Return InferTypeInArgumentList( + DirectCast(argument.Parent, ArgumentListSyntax), argument, previousToken) + End If + + If TypeOf argument.Parent Is TupleExpressionSyntax Then + Return InferTypeInTupleExpression( + DirectCast(argument.Parent, TupleExpressionSyntax), + DirectCast(argument, SimpleArgumentSyntax)) + End If + + Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo) + End Function + + Private Function InferTypeInTupleExpression(tupleExpression As TupleExpressionSyntax, + argument As SimpleArgumentSyntax) As IEnumerable(Of TypeInferenceInfo) + Dim index = tupleExpression.Arguments.IndexOf(argument) + Dim parentTypes = InferTypes(tupleExpression) + + Return parentTypes.Select(Function(TypeInfo) TypeInfo.InferredType). + OfType(Of INamedTypeSymbol)(). + Where(Function(namedType) namedType.IsTupleType AndAlso index < namedType.TupleElements.Length). + Select(Function(tupleType) New TypeInferenceInfo(tupleType.TupleElements(index).Type)) + End Function + Private Function InferTypeInArgumentList(argumentList As ArgumentListSyntax, - Optional argumentOpt As ArgumentSyntax = Nothing, Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo) + Optional argumentOpt As ArgumentSyntax = Nothing, + Optional previousToken As SyntaxToken = Nothing) As IEnumerable(Of TypeInferenceInfo) If argumentList Is Nothing Then Return SpecializedCollections.EmptyEnumerable(Of TypeInferenceInfo)() End If If argumentList.Parent IsNot Nothing Then + If argumentList.IsParentKind(SyntaxKind.ArrayCreationExpression) Then Return SpecializedCollections.SingletonEnumerable(New TypeInferenceInfo(Compilation.GetSpecialType(SpecialType.System_Int32))) ElseIf argumentList.IsParentKind(SyntaxKind.InvocationExpression) Then From f0499d26d10ddaa1d56ef29ef19f3121d89f8954 Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Wed, 3 May 2017 16:26:49 -0700 Subject: [PATCH 138/214] Disable WorkspacesNetCore.ProjectReference Related to #19223 --- .../IntegrationTests/Workspace/WorkspacesNetCore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/Workspace/WorkspacesNetCore.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/Workspace/WorkspacesNetCore.cs index 3ac2153f492a0..59efc22b89791 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/Workspace/WorkspacesNetCore.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/Workspace/WorkspacesNetCore.cs @@ -41,7 +41,7 @@ public override void MetadataReference() base.MetadataReference(); } - [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/19223"), Trait(Traits.Feature, Traits.Features.Workspace)] public override void ProjectReference() { base.ProjectReference(); From 084cc62e077a9d51ac8593f528b9c7c33a506952 Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Thu, 4 May 2017 10:25:44 -0700 Subject: [PATCH 139/214] Address PR feedback from Chuck --- src/Compilers/CSharp/Portable/BoundTree/Statement.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs index 7a3bc47f1f1ba..c8f3c3f1f7401 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Statement.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Statement.cs @@ -876,7 +876,7 @@ protected override ImmutableArray Children { get { - var builder = ImmutableArray.CreateBuilder(this.SwitchSections.Length + 2); + var builder = ImmutableArray.CreateBuilder(this.SwitchSections.Length + 1); builder.Add(this.Expression); foreach (var section in this.SwitchSections) { @@ -902,7 +902,7 @@ public override TResult Accept(OperationVisitor Children => this.SwitchLabels.As().AddRange(this.Statements).ToImmutableArray(); + protected override ImmutableArray Children => this.SwitchLabels.As().AddRange(this.Statements); } partial class BoundPatternSwitchLabel From 3881e9581be3806c24d2cffc0ec1b9492d5e52ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Thu, 4 May 2017 12:42:41 -0700 Subject: [PATCH 140/214] Debug info for unreachable constants and variables (#19236) --- .../Test/Emit/PDB/PDBDynamicLocalsTests.cs | 226 +++++++++++++++--- .../CSharp/Test/Emit/PDB/PDBTupleTests.cs | 79 ++++++ .../PEWriter/CustomDebugInfoWriter.cs | 18 +- .../Test/Emit/PDB/PDBTupleTests.vb | 96 +++++++- 4 files changed, 363 insertions(+), 56 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBDynamicLocalsTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBDynamicLocalsTests.cs index 64cda47af677a..0089eae82ad09 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBDynamicLocalsTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBDynamicLocalsTests.cs @@ -1184,7 +1184,7 @@ public static void Main(string[] args) { dynamic dInFor; } - for (dynamic d = ""1""; ;) + for (dynamic d = ""1""; d1 < 0;) { //do nothing } @@ -1235,19 +1235,19 @@ from score in scores - - - - - + + + + + - - + + @@ -1260,17 +1260,18 @@ from score in scores - - - - - - + + + + + + + 0 - - + + @@ -1309,18 +1310,37 @@ from score in scores - + - - - - - - + + + + + + @@ -1339,8 +1359,23 @@ from score in scores - - + + + + + + + + + + + + + + + + + @@ -1364,7 +1399,7 @@ from score in scores "); } - [Fact, WorkItem(17947, "https://github.com/dotnet/roslyn/issues/17947")] + [Fact] public void EmitPDBLangConstructsLocalConstants() { string source = @" @@ -1397,7 +1432,7 @@ public static void Main(string[] args) { const dynamic dInFor = null; } - for (dynamic d = ""1""; ;) + for (dynamic d = ""1""; d1 < 0;) { //do nothing } @@ -1427,8 +1462,6 @@ public static void Main(string[] args) } }"; - // BUG: note that dInIf, dInElse, dInTry, dInCatch, dInFinally, scoreQuery1, scoreQuery2 are missing from - var c = CreateCompilationWithMscorlibAndSystemCore(source, options: TestOptions.DebugDll); c.VerifyPdb(@" @@ -1439,7 +1472,6 @@ public static void Main(string[] args) - @@ -1448,6 +1480,12 @@ public static void Main(string[] args) + + + + + + @@ -1460,7 +1498,8 @@ public static void Main(string[] args) - + + @@ -1497,14 +1536,31 @@ public static void Main(string[] args) - + - - + + @@ -1527,15 +1583,107 @@ public static void Main(string[] args) - - + + + + + + + + + + + + + + + + + "); } - + + [Fact, WorkItem(17947, "https://github.com/dotnet/roslyn/issues/17947")] + public void VariablesAndConstantsInUnreachableCode() + { + string source = @" +class C +{ + void F() + { + dynamic v1 = 1; + const dynamic c1 = null; + + throw null; + + dynamic v2 = 1; + const dynamic c2 = null; + + { + dynamic v3 = 1; + const dynamic c3 = null; + } + } +} +"; + var c = CreateStandardCompilation(source, options: TestOptions.DebugDll); + var v = CompileAndVerify(c); + v.VerifyIL("C.F", @" +{ + // Code size 10 (0xa) + .maxstack 1 + .locals init (object V_0, //v1 + object V_1, //v2 + object V_2) //v3 + IL_0000: nop + IL_0001: ldc.i4.1 + IL_0002: box ""int"" + IL_0007: stloc.0 + IL_0008: ldnull + IL_0009: throw +}"); + + c.VerifyPdb(@" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } + [Fact] public void EmitPDBVarVariableLocal() { diff --git a/src/Compilers/CSharp/Test/Emit/PDB/PDBTupleTests.cs b/src/Compilers/CSharp/Test/Emit/PDB/PDBTupleTests.cs index ae5055818c69d..2a9cb23531aef 100644 --- a/src/Compilers/CSharp/Test/Emit/PDB/PDBTupleTests.cs +++ b/src/Compilers/CSharp/Test/Emit/PDB/PDBTupleTests.cs @@ -2,6 +2,7 @@ using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.PDB @@ -262,5 +263,83 @@ static void F(System.Collections.Generic.IEnumerable<(int a, int b)> ie) ")); } + + [Fact, WorkItem(17947, "https://github.com/dotnet/roslyn/issues/17947")] + public void VariablesAndConstantsInUnreachableCode() + { + string source = @" +class C +{ + void F() + { + (int a, int b)[] v1 = null; + const (int a, int b)[] c1 = null; + + throw null; + + (int a, int b)[] v2 = null; + const (int a, int b)[] c2 = null; + + { + (int a, int b)[] v3 = null; + const (int a, int b)[] c3 = null; + } + } +} +"; + var c = CreateStandardCompilation(source, new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugDll); + var v = CompileAndVerify(c); + v.VerifyIL("C.F", @" +{ + // Code size 5 (0x5) + .maxstack 1 + .locals init ((int a, int b)[] V_0, //v1 + (int a, int b)[] V_1, //v2 + (int a, int b)[] V_2) //v3 + IL_0000: nop + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: ldnull + IL_0004: throw +} +"); + + c.VerifyPdb(@" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +"); + } } } diff --git a/src/Compilers/Core/Portable/PEWriter/CustomDebugInfoWriter.cs b/src/Compilers/Core/Portable/PEWriter/CustomDebugInfoWriter.cs index 3afb5050608ee..5669ec2c1fc71 100644 --- a/src/Compilers/Core/Portable/PEWriter/CustomDebugInfoWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/CustomDebugInfoWriter.cs @@ -141,21 +141,21 @@ private static ArrayBuilder GetLocalInfoToSerialize( { ArrayBuilder builder = null; - foreach (var local in methodBody.LocalVariables) + foreach (var currentScope in methodBody.LocalScopes) { - Debug.Assert(local.SlotIndex >= 0); - if (filter(local)) + foreach (var local in currentScope.Variables) { - if (builder == null) + Debug.Assert(local.SlotIndex >= 0); + if (filter(local)) { - builder = ArrayBuilder.GetInstance(); + if (builder == null) + { + builder = ArrayBuilder.GetInstance(); + } + builder.Add(getInfo(default(LocalScope), local)); } - builder.Add(getInfo(default(LocalScope), local)); } - } - foreach (var currentScope in methodBody.LocalScopes) - { foreach (var localConstant in currentScope.Constants) { Debug.Assert(localConstant.SlotIndex < 0); diff --git a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTupleTests.vb b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTupleTests.vb index 3c2dee5be373d..c78d3704c08d5 100644 --- a/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTupleTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/PDB/PDBTupleTests.vb @@ -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. Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Roslyn.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.PDB @@ -9,16 +10,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests.PDB Public Sub Local() - Dim source = - - - +" Dim comp = CreateCompilationWithMscorlib(source, references:={ValueTupleRef, SystemRuntimeFacadeRef}, options:=TestOptions.DebugDll) comp.VerifyPdb("C.F", @@ -33,9 +31,9 @@ End Class - - - + + + @@ -46,6 +44,88 @@ End Class ) End Sub + + Public Sub VariablesAndConstantsInUnreachableCode() + Dim source = " +Imports System +Imports System.Collections.Generic + +Class C(Of T) + Enum E + A + End Enum + + Sub F() + Dim v1 As C(Of (a As Integer, b As Integer)).E = Nothing + Const c1 As C(Of (a As Integer, b As Integer)).E = Nothing + + Throw New Exception() + + Dim v2 As C(Of (a As Integer, b As Integer)).E = Nothing + Const c2 As C(Of (a As Integer, b As Integer)).E = Nothing + + Do + Dim v3 As C(Of (a As Integer, b As Integer)).E = Nothing + Const c3 As C(Of (a As Integer, b As Integer)).E = Nothing + Loop + End Sub +End Class +" + Dim c = CreateCompilationWithMscorlib(source, references:={ValueTupleRef, SystemRuntimeFacadeRef}, options:=TestOptions.DebugDll) + + Dim v = CompileAndVerify(c) + v.VerifyIL("C(Of T).F()", " +{ + // Code size 9 (0x9) + .maxstack 1 + .locals init (C(Of (a As Integer, b As Integer)).E V_0, //v1 + C(Of (a As Integer, b As Integer)).E V_1, //v2 + C(Of (a As Integer, b As Integer)).E V_2) //v3 + IL_0000: nop + IL_0001: ldc.i4.0 + IL_0002: stloc.0 + IL_0003: newobj ""Sub System.Exception..ctor()"" + IL_0008: throw +} +") + + c.VerifyPdb( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) + End Sub + End Class End Namespace From a2de1326241c7cb83c1e3d7b69115267cd5cba2b Mon Sep 17 00:00:00 2001 From: Ivan Basov Date: Thu, 4 May 2017 13:02:27 -0700 Subject: [PATCH 141/214] support of C# 7.0 foreach in ENC (#19114) --- .../EditAndContinue/EditAndContinueTests.cs | 224 ++++++++++ .../EditAndContinue/LocalSlotMappingTests.cs | 86 ++++ .../EditAndContinue/ActiveStatementTests.cs | 383 +++++++++++++++++- .../CSharpEditAndContinueAnalyzer.cs | 95 +++-- .../StatementSyntaxComparer.cs | 2 +- .../AbstractEditAndContinueAnalyzer.cs | 10 +- .../VisualBasicEditAndContinueAnalyzer.vb | 8 +- 7 files changed, 760 insertions(+), 48 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 57e5cb2a14f3a..85712044bdcfe 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -7851,6 +7851,230 @@ .locals init (int V_0, //x IL_0012: ldloc.s V_6 IL_0014: ret } +"); + } + + [Fact] + public void ForeachStatement() + { + var source0 = MarkedSource(@" +class C +{ + public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) }; + + public static void G() + { + foreach (var (x, (y, z)) in F()) + { + System.Console.WriteLine(x); + } + } +}"); + var source1 = MarkedSource(@" +class C +{ + public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) }; + + public static void G() + { + foreach (var (x1, (y, z)) in F()) + { + System.Console.WriteLine(x1); + } + } +}"); + + var source2 = MarkedSource(@" +class C +{ + public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) }; + + public static void G() + { + foreach (var (x1, yz) in F()) + { + System.Console.WriteLine(x1); + } + } +}"); + + var compilation0 = CreateStandardCompilation(source0.Tree, options: ComSafeDebugDll, references: s_valueTupleRefs); + var compilation1 = compilation0.WithSource(source1.Tree); + var compilation2 = compilation1.WithSource(source2.Tree); + + var f0 = compilation0.GetMember("C.G"); + var f1 = compilation1.GetMember("C.G"); + var f2 = compilation2.GetMember("C.G"); + + var v0 = CompileAndVerify(compilation0); + v0.VerifyIL("C.G", @" +{ + // Code size 72 (0x48) + .maxstack 2 + .locals init ((int, (bool, double))[] V_0, + int V_1, + int V_2, //x + bool V_3, //y + double V_4, //z + System.ValueTuple V_5) + IL_0000: nop + IL_0001: nop + IL_0002: call ""(int, (bool, double))[] C.F()"" + IL_0007: stloc.0 + IL_0008: ldc.i4.0 + IL_0009: stloc.1 + IL_000a: br.s IL_0041 + IL_000c: ldloc.0 + IL_000d: ldloc.1 + IL_000e: ldelem ""System.ValueTuple"" + IL_0013: dup + IL_0014: ldfld ""(bool, double) System.ValueTuple.Item2"" + IL_0019: stloc.s V_5 + IL_001b: dup + IL_001c: ldfld ""int System.ValueTuple.Item1"" + IL_0021: stloc.2 + IL_0022: ldloc.s V_5 + IL_0024: ldfld ""bool System.ValueTuple.Item1"" + IL_0029: stloc.3 + IL_002a: ldloc.s V_5 + IL_002c: ldfld ""double System.ValueTuple.Item2"" + IL_0031: stloc.s V_4 + IL_0033: pop + IL_0034: nop + IL_0035: ldloc.2 + IL_0036: call ""void System.Console.WriteLine(int)"" + IL_003b: nop + IL_003c: nop + IL_003d: ldloc.1 + IL_003e: ldc.i4.1 + IL_003f: add + IL_0040: stloc.1 + IL_0041: ldloc.1 + IL_0042: ldloc.0 + IL_0043: ldlen + IL_0044: conv.i4 + IL_0045: blt.s IL_000c + IL_0047: ret +} +"); + + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + new SemanticEdit(SemanticEditKind.Update, f0, f1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + diff1.VerifyIL("C.G", @" +{ + // Code size 80 (0x50) + .maxstack 2 + .locals init ([unchanged] V_0, + [int] V_1, + int V_2, //x1 + bool V_3, //y + double V_4, //z + [unchanged] V_5, + (int, (bool, double))[] V_6, + int V_7, + System.ValueTuple V_8) + IL_0000: nop + IL_0001: nop + IL_0002: call ""(int, (bool, double))[] C.F()"" + IL_0007: stloc.s V_6 + IL_0009: ldc.i4.0 + IL_000a: stloc.s V_7 + IL_000c: br.s IL_0047 + IL_000e: ldloc.s V_6 + IL_0010: ldloc.s V_7 + IL_0012: ldelem ""System.ValueTuple"" + IL_0017: dup + IL_0018: ldfld ""(bool, double) System.ValueTuple.Item2"" + IL_001d: stloc.s V_8 + IL_001f: dup + IL_0020: ldfld ""int System.ValueTuple.Item1"" + IL_0025: stloc.2 + IL_0026: ldloc.s V_8 + IL_0028: ldfld ""bool System.ValueTuple.Item1"" + IL_002d: stloc.3 + IL_002e: ldloc.s V_8 + IL_0030: ldfld ""double System.ValueTuple.Item2"" + IL_0035: stloc.s V_4 + IL_0037: pop + IL_0038: nop + IL_0039: ldloc.2 + IL_003a: call ""void System.Console.WriteLine(int)"" + IL_003f: nop + IL_0040: nop + IL_0041: ldloc.s V_7 + IL_0043: ldc.i4.1 + IL_0044: add + IL_0045: stloc.s V_7 + IL_0047: ldloc.s V_7 + IL_0049: ldloc.s V_6 + IL_004b: ldlen + IL_004c: conv.i4 + IL_004d: blt.s IL_000e + IL_004f: ret +} +"); + + var diff2 = compilation2.EmitDifference( + diff1.NextGeneration, + ImmutableArray.Create( + new SemanticEdit(SemanticEditKind.Update, f1, f2, GetSyntaxMapFromMarkers(source1, source2), preserveLocalVariables: true))); + + diff2.VerifyIL("C.G", @" +{ + // Code size 66 (0x42) + .maxstack 2 + .locals init ([unchanged] V_0, + [int] V_1, + int V_2, //x1 + [bool] V_3, + [unchanged] V_4, + [unchanged] V_5, + [unchanged] V_6, + [int] V_7, + [unchanged] V_8, + (int, (bool, double))[] V_9, + int V_10, + System.ValueTuple V_11, //yz + System.ValueTuple V_12) + IL_0000: nop + IL_0001: nop + IL_0002: call ""(int, (bool, double))[] C.F()"" + IL_0007: stloc.s V_9 + IL_0009: ldc.i4.0 + IL_000a: stloc.s V_10 + IL_000c: br.s IL_0039 + IL_000e: ldloc.s V_9 + IL_0010: ldloc.s V_10 + IL_0012: ldelem ""System.ValueTuple"" + IL_0017: stloc.s V_12 + IL_0019: ldloc.s V_12 + IL_001b: ldfld ""int System.ValueTuple.Item1"" + IL_0020: stloc.2 + IL_0021: ldloc.s V_12 + IL_0023: ldfld ""(bool, double) System.ValueTuple.Item2"" + IL_0028: stloc.s V_11 + IL_002a: nop + IL_002b: ldloc.2 + IL_002c: call ""void System.Console.WriteLine(int)"" + IL_0031: nop + IL_0032: nop + IL_0033: ldloc.s V_10 + IL_0035: ldc.i4.1 + IL_0036: add + IL_0037: stloc.s V_10 + IL_0039: ldloc.s V_10 + IL_003b: ldloc.s V_9 + IL_003d: ldlen + IL_003e: conv.i4 + IL_003f: blt.s IL_000e + IL_0041: ret +} "); } } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs index ddc86dae03da2..7dce777325afd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/LocalSlotMappingTests.cs @@ -3433,6 +3433,92 @@ .locals init (bool V_0, -IL_0025: ldloc.2 IL_0026: ret } +", methodToken: diff1.UpdatedMethods.Single()); + } + + [Fact] + public void ForEachStatement_Deconstruction() + { + var source = @" +class C +{ + public static (int, (bool, double))[] F() => new[] { (1, (true, 2.0)) }; + + public static void G() + { + foreach (var (x, (y, z)) in F()) + { + System.Console.WriteLine(x); + } + } +}"; + + var compilation0 = CreateStandardCompilation(source, options: TestOptions.DebugDll, references: s_valueTupleRefs); + var compilation1 = compilation0.WithSource(source); + + var testData0 = new CompilationTestData(); + var bytes0 = compilation0.EmitToArray(testData: testData0); + var methodData0 = testData0.GetMethodData("C.G"); + var method0 = compilation0.GetMember("C.G"); + var generation0 = EmitBaseline.CreateInitialBaseline(ModuleMetadata.CreateFromImage(bytes0), methodData0.EncDebugInfoProvider()); + + var method1 = compilation1.GetMember("C.G"); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(new SemanticEdit(SemanticEditKind.Update, method0, method1, GetEquivalentNodesMap(method1, method0), preserveLocalVariables: true))); + + diff1.VerifyIL("C.G", @" +{ + // Code size 80 (0x50) + .maxstack 2 + .locals init ([unchanged] V_0, + [int] V_1, + int V_2, //x + bool V_3, //y + double V_4, //z + [unchanged] V_5, + (int, (bool, double))[] V_6, + int V_7, + System.ValueTuple V_8) + -IL_0000: nop + -IL_0001: nop + -IL_0002: call ""(int, (bool, double))[] C.F()"" + IL_0007: stloc.s V_6 + IL_0009: ldc.i4.0 + IL_000a: stloc.s V_7 + ~IL_000c: br.s IL_0047 + -IL_000e: ldloc.s V_6 + IL_0010: ldloc.s V_7 + IL_0012: ldelem ""System.ValueTuple"" + IL_0017: dup + IL_0018: ldfld ""(bool, double) System.ValueTuple.Item2"" + IL_001d: stloc.s V_8 + IL_001f: dup + IL_0020: ldfld ""int System.ValueTuple.Item1"" + IL_0025: stloc.2 + IL_0026: ldloc.s V_8 + IL_0028: ldfld ""bool System.ValueTuple.Item1"" + IL_002d: stloc.3 + IL_002e: ldloc.s V_8 + IL_0030: ldfld ""double System.ValueTuple.Item2"" + IL_0035: stloc.s V_4 + IL_0037: pop + -IL_0038: nop + -IL_0039: ldloc.2 + IL_003a: call ""void System.Console.WriteLine(int)"" + IL_003f: nop + -IL_0040: nop + ~IL_0041: ldloc.s V_7 + IL_0043: ldc.i4.1 + IL_0044: add + IL_0045: stloc.s V_7 + -IL_0047: ldloc.s V_7 + IL_0049: ldloc.s V_6 + IL_004b: ldlen + IL_004c: conv.i4 + IL_004d: blt.s IL_000e + -IL_004f: ret +} ", methodToken: diff1.UpdatedMethods.Single()); } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index 52366c79d5517..a67a29f425690 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -3184,6 +3184,41 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariableBody_Update_ExpressionActive() + { + string src1 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(0); + } + } +}"; + string src2 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(1); + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEachBody_Update_InKeywordActive() { @@ -3219,6 +3254,41 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariableBody_Update_InKeywordActive() + { + string src1 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(0); + } + } +}"; + string src2 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(1); + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEachBody_Update_VariableActive() { @@ -3254,6 +3324,41 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariableBody_Update_VariableActive() + { + string src1 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(0); + } + } +}"; + string src2 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(1); + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEachBody_Update_ForeachKeywordActive() { @@ -3289,6 +3394,41 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariableBody_Update_ForeachKeywordActive() + { + string src1 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(0); + } + } +}"; + string src2 = @" +class Test +{ + private static (string, int) F() { return null; } + + static void Main(string[] args) + { + foreach ((string s, int i) in F()) + { + System.Console.Write(1); + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEachVariable_Update() { @@ -3328,7 +3468,44 @@ static void Main(string[] args) } [Fact] - public void ForEach_Reorder_Leaf1() + public void ForEachDeconstructionVariable_Update() + { + string src1 = @" +class Test +{ + private static (int, (bool, double))[] F() { return new[] { (1, (true, 2.0)) }; } + + static void Main(string[] args) + { + foreach ((int i, (bool b, double d)) in F()) + { + System.Console.Write(0); + } + } +}"; + string src2 = @" +class Test +{ + private static (int, (bool, double))[] F() { return new[] { (1, (true, 2.0)) }; } + + static void Main(string[] args) + { + foreach ((int i, (var b, double d)) in F()) + { + System.Console.Write(1); + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "(int i, (var b, double d))"), + Diagnostic(RudeEditKind.UpdateAroundActiveStatement, "foreach ( (int i, (var b, double d)) in F())", CSharpFeaturesResources.foreach_statement)); + } + + [Fact] + public void ForEach_Reorder_Leaf() { string src1 = @" class Test @@ -3377,19 +3554,19 @@ static void Main(string[] args) } [Fact] - public void ForEach_Update_Leaf1() + public void ForEachVariable_Reorder_Leaf() { string src1 = @" class Test { - public static int[] e1 = new int[1]; - public static int[] e2 = new int[1]; + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; static void Main(string[] args) { - foreach (var a in e1) + foreach ((var a1, var a2) in e1) { - foreach (var b in e1) + foreach ((int b1, bool b2) in e1) { foreach (var c in e1) { @@ -3402,16 +3579,16 @@ static void Main(string[] args) string src2 = @" class Test { - public static int[] e1 = new int[1]; - public static int[] e2 = new int[1]; + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; static void Main(string[] args) { - foreach (var b in e1) + foreach ((int b1, bool b2) in e1) { foreach (var c in e1) { - foreach (var a in e1) + foreach ((var a1, var a2) in e1) { System.Console.Write(); } @@ -3426,7 +3603,7 @@ static void Main(string[] args) } [Fact] - public void ForEach_Update_Leaf2() + public void ForEach_Update_Leaf() { string src1 = @" class Test @@ -3468,6 +3645,49 @@ static void Main(string[] args) Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach (var a in e1)", CSharpFeaturesResources.foreach_statement)); } + [Fact] + public void ForEachVariable_Update_Leaf() + { + string src1 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + System.Console.Write(); + } +}"; + string src2 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + foreach ((int b1, bool b2) in e1) + { + foreach (var c in e1) + { + foreach ((var a1, var a2) in e1) + { + System.Console.Write(); + } + } + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach (var c in e1)", CSharpFeaturesResources.foreach_statement), + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach ((int b1, bool b2) in e1)", CSharpFeaturesResources.foreach_statement), + Diagnostic(RudeEditKind.InsertAroundActiveStatement, "foreach ((var a1, var a2) in e1)", CSharpFeaturesResources.foreach_statement)); + } + [Fact] public void ForEach_Delete_Leaf1() { @@ -3514,6 +3734,52 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariable_Delete_Leaf1() + { + string src1 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + foreach ((var a1, var a2) in e1) + { + foreach ((int b1, bool b2) in e1) + { + foreach (var c in e1) + { + System.Console.Write(); + } + } + } + } +}"; + string src2 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + foreach ((var a1, var a2) in e1) + { + foreach ((int b1, bool b2) in e1) + { + System.Console.Write(); + } + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEach_Delete_Leaf2() { @@ -3560,6 +3826,52 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariable_Delete_Leaf2() + { + string src1 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + foreach ((var a1, var a2) in e1) + { + foreach ((int b1, bool b2) in e1) + { + foreach (var c in e1) + { + System.Console.Write(); + } + } + } + } +}"; + string src2 = @" +class Test +{ + public static (int, bool)[] e1 = new (int, bool)[1]; + public static (int, bool)[] e2 = new (int, bool)[1]; + + static void Main(string[] args) + { + foreach ((int b1, bool b2) in e1) + { + foreach (var c in e1) + { + System.Console.Write(); + } + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEach_Delete_Leaf3() { @@ -3606,6 +3918,52 @@ static void Main(string[] args) edits.VerifyRudeDiagnostics(active); } + [Fact] + public void ForEachVariable_Delete_Leaf3() + { + string src1 = @" +class Test +{ + public static int[] e1 = new int[1]; + public static int[] e2 = new int[1]; + + static void Main(string[] args) + { + foreach ((var a1, var a2) in e1) + { + foreach ((int b1, bool b2) in e1) + { + foreach (var c in e1) + { + System.Console.Write(); + } + } + } + } +}"; + string src2 = @" +class Test +{ + public static int[] e1 = new int[1]; + public static int[] e2 = new int[1]; + + static void Main(string[] args) + { + foreach ((var a1, var a2) in e1) + { + foreach (var c in e1) + { + System.Console.Write(); + } + } + } +}"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + [Fact] public void ForEach_Lambda1() { @@ -8182,8 +8540,7 @@ static void F(object o) var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active, - Diagnostic(RudeEditKind.UpdateAroundActiveStatement, "var (x, y)", CSharpFeaturesResources.deconstruction)); + edits.VerifyRudeDiagnostics(active); } [Fact] diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index a49f7d2650dd7..a60a91374a1ba 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1,6 +1,5 @@ // 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; @@ -306,7 +305,8 @@ protected override SyntaxNode FindStatementAndPartner(SyntaxNode declarationBody break; case SyntaxKind.ForEachStatement: - statementPart = (int)GetStatementPart((ForEachStatementSyntax)node, position); + case SyntaxKind.ForEachVariableStatement: + statementPart = (int)GetStatementPart((CommonForEachStatementSyntax)node, position); break; case SyntaxKind.VariableDeclaration: @@ -352,7 +352,7 @@ private static TextSpan GetActiveSpan(BlockSyntax node, BlockPart part) } } - private static ForEachPart GetStatementPart(ForEachStatementSyntax node, int position) + private static ForEachPart GetStatementPart(CommonForEachStatementSyntax node, int position) { return position < node.OpenParenToken.SpanStart ? ForEachPart.ForEach : position < node.InKeyword.SpanStart ? ForEachPart.VariableDeclaration : @@ -381,6 +381,27 @@ private static TextSpan GetActiveSpan(ForEachStatementSyntax node, ForEachPart p } } + private static TextSpan GetActiveSpan(ForEachVariableStatementSyntax node, ForEachPart part) + { + switch (part) + { + case ForEachPart.ForEach: + return node.ForEachKeyword.Span; + + case ForEachPart.VariableDeclaration: + return TextSpan.FromBounds(node.Variable.SpanStart, node.Variable.Span.End); + + case ForEachPart.In: + return node.InKeyword.Span; + + case ForEachPart.Expression: + return node.Expression.Span; + + default: + throw ExceptionUtilities.UnexpectedValue(part); + } + } + protected override bool AreEquivalent(SyntaxNode left, SyntaxNode right) { return SyntaxFactory.AreEquivalent(left, right); @@ -601,6 +622,10 @@ protected override bool TryGetActiveSpan(SyntaxNode node, int statementPart, out span = GetActiveSpan((ForEachStatementSyntax)node, (ForEachPart)statementPart); return true; + case SyntaxKind.ForEachVariableStatement: + span = GetActiveSpan((ForEachVariableStatementSyntax)node, (ForEachPart)statementPart); + return true; + case SyntaxKind.DoStatement: // The active statement of DoStatement node is the while condition, // which is lexically not the closest breakpoint span (the body is). @@ -732,10 +757,11 @@ protected override bool AreEquivalentActiveStatements(SyntaxNode oldStatement, S return true; case SyntaxKind.ForEachStatement: + case SyntaxKind.ForEachVariableStatement: Debug.Assert(statementPart != 0); // only check the expression, edits in the body and the variable declaration are allowed: - return AreEquivalentActiveStatements((ForEachStatementSyntax)oldStatement, (ForEachStatementSyntax)newStatement); + return AreEquivalentActiveStatements((CommonForEachStatementSyntax)oldStatement, (CommonForEachStatementSyntax)newStatement); case SyntaxKind.IfStatement: // only check the condition, edits in the body are allowed: @@ -807,11 +833,30 @@ private static bool AreEquivalentActiveStatements(UsingStatementSyntax oldNode, (SyntaxNode)newNode.Declaration ?? newNode.Expression); } - private static bool AreEquivalentActiveStatements(ForEachStatementSyntax oldNode, ForEachStatementSyntax newNode) + private static bool AreEquivalentActiveStatements(CommonForEachStatementSyntax oldNode, CommonForEachStatementSyntax newNode) { - // This is conservative, we might be able to allow changing the type. - return AreEquivalentIgnoringLambdaBodies(oldNode.Type, newNode.Type) - && AreEquivalentIgnoringLambdaBodies(oldNode.Expression, newNode.Expression); + if (oldNode.Kind() != newNode.Kind() || !AreEquivalentIgnoringLambdaBodies(oldNode.Expression, newNode.Expression)) + { + return false; + } + + switch (oldNode.Kind()) + { + case SyntaxKind.ForEachStatement: return AreEquivalentIgnoringLambdaBodies(((ForEachStatementSyntax)oldNode).Type, ((ForEachStatementSyntax)newNode).Type); + case SyntaxKind.ForEachVariableStatement: return AreEquivalentIgnoringLambdaBodies(((ForEachVariableStatementSyntax)oldNode).Variable, ((ForEachVariableStatementSyntax)newNode).Variable); + default: throw ExceptionUtilities.UnexpectedValue(oldNode.Kind()); + } + } + + private static bool AreSimilarActiveStatements(CommonForEachStatementSyntax oldNode, CommonForEachStatementSyntax newNode) + { + List oldTokens = null; + List newTokens = null; + + StatementSyntaxComparer.GetLocalNames(oldNode, ref oldTokens); + StatementSyntaxComparer.GetLocalNames(newNode, ref newTokens); + + return DeclareSameIdentifiers(oldTokens.ToArray(), newTokens.ToArray()); } internal override bool IsMethod(SyntaxNode declaration) @@ -1259,8 +1304,9 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node, return TextSpan.FromBounds(forStatement.ForKeyword.SpanStart, forStatement.CloseParenToken.Span.End); case SyntaxKind.ForEachStatement: - var forEachStatement = (ForEachStatementSyntax)node; - return TextSpan.FromBounds(forEachStatement.ForEachKeyword.SpanStart, forEachStatement.CloseParenToken.Span.End); + case SyntaxKind.ForEachVariableStatement: + var commonForEachStatement = (CommonForEachStatementSyntax)node; + return TextSpan.FromBounds(commonForEachStatement.ForEachKeyword.SpanStart, commonForEachStatement.CloseParenToken.Span.End); case SyntaxKind.LabeledStatement: return ((LabeledStatementSyntax)node).Identifier.Span; @@ -1342,9 +1388,6 @@ internal static TextSpan GetDiagnosticSpanImpl(SyntaxKind kind, SyntaxNode node, case SyntaxKind.GroupClause: return ((GroupClauseSyntax)node).GroupKeyword.Span; - case SyntaxKind.ForEachVariableStatement: - return ((ForEachVariableStatementSyntax)node).Variable.Span; - case SyntaxKind.IsPatternExpression: case SyntaxKind.TupleType: case SyntaxKind.TupleExpression: @@ -1549,6 +1592,7 @@ internal static string GetStatementDisplayNameImpl(SyntaxNode node) return CSharpFeaturesResources.lock_statement; case SyntaxKind.ForEachStatement: + case SyntaxKind.ForEachVariableStatement: return CSharpFeaturesResources.foreach_statement; case SyntaxKind.CheckedStatement: @@ -1604,9 +1648,6 @@ internal static string GetStatementDisplayNameImpl(SyntaxNode node) case SyntaxKind.IsPatternExpression: return CSharpFeaturesResources.is_pattern; - case SyntaxKind.ForEachVariableStatement: - return CSharpFeaturesResources.deconstruction; - case SyntaxKind.SimpleAssignmentExpression: if (((AssignmentExpressionSyntax)node).IsDeconstruction()) { @@ -3108,7 +3149,6 @@ private static bool IsUnsupportedCSharp7EnCNode(SyntaxNode n) { switch (n.Kind()) { - case SyntaxKind.ForEachVariableStatement: case SyntaxKind.LocalFunctionStatement: return true; default: @@ -3187,15 +3227,15 @@ private void ReportRudeEditsForAncestorsDeclaringInterStatementTemps( // // Unlike exception regions matching where we use LCS, we allow reordering of the statements. - ReportUnmatchedStatements(diagnostics, match, (int)SyntaxKind.LockStatement, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(diagnostics, match, new[] { (int)SyntaxKind.LockStatement }, oldActiveStatement, newActiveStatement, areEquivalent: AreEquivalentActiveStatements, areSimilar: null); - ReportUnmatchedStatements(diagnostics, match, (int)SyntaxKind.FixedStatement, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(diagnostics, match, new[] { (int)SyntaxKind.FixedStatement }, oldActiveStatement, newActiveStatement, areEquivalent: AreEquivalentActiveStatements, areSimilar: (n1, n2) => DeclareSameIdentifiers(n1.Declaration.Variables, n2.Declaration.Variables)); - ReportUnmatchedStatements(diagnostics, match, (int)SyntaxKind.UsingStatement, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(diagnostics, match, new[] { (int)SyntaxKind.UsingStatement }, oldActiveStatement, newActiveStatement, areEquivalent: AreEquivalentActiveStatements, areSimilar: (using1, using2) => { @@ -3203,21 +3243,26 @@ private void ReportRudeEditsForAncestorsDeclaringInterStatementTemps( DeclareSameIdentifiers(using1.Declaration.Variables, using2.Declaration.Variables); }); - ReportUnmatchedStatements(diagnostics, match, (int)SyntaxKind.ForEachStatement, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(diagnostics, match, new[] { (int)SyntaxKind.ForEachStatement, (int)SyntaxKind.ForEachVariableStatement }, oldActiveStatement, newActiveStatement, areEquivalent: AreEquivalentActiveStatements, - areSimilar: (n1, n2) => SyntaxFactory.AreEquivalent(n1.Identifier, n2.Identifier)); + areSimilar: AreSimilarActiveStatements); } private static bool DeclareSameIdentifiers(SeparatedSyntaxList oldVariables, SeparatedSyntaxList newVariables) { - if (oldVariables.Count != newVariables.Count) + return DeclareSameIdentifiers(oldVariables.Select(v => v.Identifier).ToArray(), newVariables.Select(v => v.Identifier).ToArray()); + } + + private static bool DeclareSameIdentifiers(SyntaxToken[] oldVariables, SyntaxToken[] newVariables) + { + if (oldVariables.Length != newVariables.Length) { return false; } - for (int i = 0; i < oldVariables.Count; i++) + for (int i = 0; i < oldVariables.Length; i++) { - if (!SyntaxFactory.AreEquivalent(oldVariables[i].Identifier, newVariables[i].Identifier)) + if (!SyntaxFactory.AreEquivalent(oldVariables[i], newVariables[i])) { return false; } diff --git a/src/Features/CSharp/Portable/EditAndContinue/StatementSyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/StatementSyntaxComparer.cs index f4700d61ebfd5..cb58a82f1cf29 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/StatementSyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/StatementSyntaxComparer.cs @@ -1006,7 +1006,7 @@ private static void GetLocalNames(VariableDeclarationSyntax localDeclaration, re } } - private static void GetLocalNames(CommonForEachStatementSyntax commonForEach, ref List result) + internal static void GetLocalNames(CommonForEachStatementSyntax commonForEach, ref List result) { switch (commonForEach.Kind()) { diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 2126145774738..d8f76241727ea 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -1696,7 +1696,7 @@ protected void AddRudeDeleteAroundActiveStatement(List diagn protected void ReportUnmatchedStatements( List diagnostics, Match match, - int syntaxKind, + int[] syntaxKinds, SyntaxNode oldActiveStatement, SyntaxNode newActiveStatement, Func areEquivalent, @@ -1704,8 +1704,8 @@ protected void ReportUnmatchedStatements( where TSyntaxNode : SyntaxNode { List oldNodes = null, newNodes = null; - GetAncestors(GetEncompassingAncestor(match.OldRoot), oldActiveStatement, syntaxKind, ref oldNodes); - GetAncestors(GetEncompassingAncestor(match.NewRoot), newActiveStatement, syntaxKind, ref newNodes); + GetAncestors(GetEncompassingAncestor(match.OldRoot), oldActiveStatement, syntaxKinds, ref oldNodes); + GetAncestors(GetEncompassingAncestor(match.NewRoot), newActiveStatement, syntaxKinds, ref newNodes); if (newNodes != null) { @@ -1836,11 +1836,11 @@ private static int IndexOfEquivalent(SyntaxNode newNode, List list) + private static void GetAncestors(SyntaxNode root, SyntaxNode node, int[] syntaxKinds, ref List list) { while (node != root) { - if (node.RawKind == syntaxKind) + if (syntaxKinds.Contains(node.RawKind)) { if (list == null) { diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 4b2b269fd7316..9e2c5e681ddf0 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -3155,19 +3155,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ' ' Unlike exception regions matching where we use LCS, we allow reordering of the statements. - ReportUnmatchedStatements(Of SyncLockBlockSyntax)(diagnostics, match, SyntaxKind.SyncLockBlock, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(Of SyncLockBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.SyncLockBlock}, oldActiveStatement, newActiveStatement, areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.SyncLockStatement.Expression, n2.SyncLockStatement.Expression), areSimilar:=Nothing) - ReportUnmatchedStatements(Of WithBlockSyntax)(diagnostics, match, SyntaxKind.WithBlock, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(Of WithBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.WithBlock}, oldActiveStatement, newActiveStatement, areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.WithStatement.Expression, n2.WithStatement.Expression), areSimilar:=Nothing) - ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, match, SyntaxKind.UsingBlock, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(Of UsingBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.UsingBlock}, oldActiveStatement, newActiveStatement, areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.UsingStatement.Expression, n2.UsingStatement.Expression), areSimilar:=Nothing) - ReportUnmatchedStatements(Of ForOrForEachBlockSyntax)(diagnostics, match, SyntaxKind.ForEachBlock, oldActiveStatement, newActiveStatement, + ReportUnmatchedStatements(Of ForOrForEachBlockSyntax)(diagnostics, match, New Integer() {SyntaxKind.ForEachBlock}, oldActiveStatement, newActiveStatement, areEquivalent:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(n1.ForOrForEachStatement, n2.ForOrForEachStatement), areSimilar:=Function(n1, n2) AreEquivalentIgnoringLambdaBodies(DirectCast(n1.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable, DirectCast(n2.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable)) From c7856e8feff002fedc45b4c41e4d4284595cc991 Mon Sep 17 00:00:00 2001 From: vsadov Date: Wed, 3 May 2017 17:06:51 -0700 Subject: [PATCH 142/214] Disabling some flaky tests TypingHelpDirectiveWorks() - https://github.com/dotnet/roslyn/issues/19232 WorkspacesNetCore.ProjectReference() - https://github.com/dotnet/roslyn/issues/19231 --- .../IntegrationTests/CSharp/CSharpInteractive.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpInteractive.cs b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpInteractive.cs index a7da7a931fe31..e110ea1038d48 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpInteractive.cs +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpInteractive.cs @@ -93,7 +93,7 @@ public async Task WpfInteractionAsync() VisualStudio.InteractiveWindow.SubmitText("b = null; w.Close(); w = null;"); } - [Fact] + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/19232")] public void TypingHelpDirectiveWorks() { VisualStudio.Workspace.SetUseSuggestionMode(true); From ea6935d6d90f8801329083b74c834fc957095fa0 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 13:52:34 -0700 Subject: [PATCH 143/214] Disable scanning for designer attributes in CPS projects. --- .../DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs index b62c4cf3fe5c8..43ee00a0a7c89 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/DesignerAttributeIncrementalAnalyzer.cs @@ -78,7 +78,6 @@ public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, In return; } -#if false // CPS projects do not support designer attributes. So we just skip these projects entirely. var isCPSProject = await Task.Factory.StartNew( () => vsWorkspace.IsCPSProject(project), @@ -90,7 +89,6 @@ public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, In { return; } -#endif // Try to compute this data in the remote process. If that fails, then compute // the results in the local process. From 112afcb744f3d8c0c703ffecfcd6fe92ab05964f Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Thu, 4 May 2017 14:44:43 -0700 Subject: [PATCH 144/214] Fix a small memory leak related to batching project loads Fixes https://github.com/dotnet/project-system/issues/1918. The language service implements `IVsSolutionLoadEvents.OnBeforeLoadProjectBatch` and `IVsSolutionLoadEvents.OnAfterLoadProjectBatch` to support asynchronous solution loads. While a batch is in progress we try to avoid pushing added projects to the workspace and instead store them in a list. When the batch is complete we may push all of those projects to the workspace at once. The current implementation has a memory leak. Project may be added outside of a pair of `OnBeforeLoadProjectBatch`/`OnAfterLoadProjectBatch` calls, and it is possible for projects to be added without those methods ever being called. However, during a solution load adding a project always causes us to put it in the list of batched projects. Since we only clear the list on calls to `OnBeforeLoadProjectBatch` or `OnAfterLoadProjectBatch` the user could close the project or even the whole solution and we could still be holding on to one or more projects and all of their associated memory. The fix here is to add a `bool` to track whether or not we're batching projects, and only put an added project in the list if we are. --- .../ProjectSystem/VisualStudioProjectTracker.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs index 6cd13bf588cfa..71a8ab80602d2 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProjectTracker.cs @@ -40,6 +40,13 @@ internal sealed partial class VisualStudioProjectTracker : ForegroundThreadAffin private readonly HostWorkspaceServices _workspaceServices; + /// + /// Set to true while we're batching project loads. That is, between + /// and + /// . + /// + private bool _batchingProjectLoads = false; + /// /// The list of projects loaded in this batch between and /// . @@ -282,7 +289,7 @@ internal void AddProject(AbstractProject project) { StartPushingToWorkspaceAndNotifyOfOpenDocuments(SpecializedCollections.SingletonEnumerable(project)); } - else + else if (_batchingProjectLoads) { _projectsLoadedThisBatch.Add(project); } @@ -916,6 +923,7 @@ internal void OnBeforeLoadProjectBatch(bool fIsBackgroundIdleBatch) { AssertIsForeground(); + _batchingProjectLoads = true; _projectsLoadedThisBatch.Clear(); } @@ -930,6 +938,7 @@ internal void OnAfterLoadProjectBatch(bool fIsBackgroundIdleBatch) StartPushingToWorkspaceAndNotifyOfOpenDocuments(_projectsLoadedThisBatch); } + _batchingProjectLoads = false; _projectsLoadedThisBatch.Clear(); } From 6715562b27974c03708e187b4c188c88dce1db79 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 15:08:54 -0700 Subject: [PATCH 145/214] Do a better job preserving blank lines when we convert code to using an object initializer. --- .../UseCollectionInitializerTests.cs | 35 ++++++++++++++++ .../UseObjectInitializerTests.cs | 41 +++++++++++++++++++ ...UseCollectionInitializerCodeFixProvider.cs | 2 +- ...ractUseObjectInitializerCodeFixProvider.cs | 2 +- 4 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs index a6f091f146cdd..4b6e6b6be07aa 100644 --- a/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseCollectionInitializer/UseCollectionInitializerTests.cs @@ -981,5 +981,40 @@ public void M() } }"); } + + [WorkItem(19253, "https://github.com/dotnet/roslyn/issues/19253")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCollectionInitializer)] + public async Task TestKeepBlankLinesAfter() + { + await TestInRegularAndScript1Async( +@" +using System.Collections.Generic; + +class MyClass +{ + public void Main() + { + var list = [||]new List(); + list.Add(1); + + int horse = 1; + } +}", +@" +using System.Collections.Generic; + +class MyClass +{ + public void Main() + { + var list = new List + { + 1 + }; + + int horse = 1; + } +}", ignoreTrivia: false); + } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs index 395fbfce9252e..5d42f49353f05 100644 --- a/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseObjectInitializer/UseObjectInitializerTests.cs @@ -534,6 +534,47 @@ public void M() } public string Value { get; set; } +}", ignoreTrivia: false); + } + + [WorkItem(19253, "https://github.com/dotnet/roslyn/issues/19253")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseObjectInitializer)] + public async Task TestKeepBlankLinesAfter() + { + await TestInRegularAndScript1Async( +@" +class Foo +{ + public int Bar { get; set; } +} + +class MyClass +{ + public void Main() + { + var foo = [||]new Foo(); + foo.Bar = 1; + + int horse = 1; + } +}", +@" +class Foo +{ + public int Bar { get; set; } +} + +class MyClass +{ + public void Main() + { + var foo = new Foo + { + Bar = 1 + }; + + int horse = 1; + } }", ignoreTrivia: false); } } diff --git a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs index a1a7b0f1763d6..a1b35e4d19ac4 100644 --- a/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseCollectionInitializer/AbstractUseCollectionInitializerCodeFixProvider.cs @@ -102,7 +102,7 @@ protected override async Task FixAllAsync( subEditor.ReplaceNode(statement, newStatement); foreach (var match in matches) { - subEditor.RemoveNode(match); + subEditor.RemoveNode(match, SyntaxRemoveOptions.KeepUnbalancedDirectives); } document = document.WithSyntaxRoot(subEditor.GetChangedRoot()); diff --git a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs index 9f6283f6cb76a..6054312f5878d 100644 --- a/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseObjectInitializer/AbstractUseObjectInitializerCodeFixProvider.cs @@ -101,7 +101,7 @@ protected override async Task FixAllAsync( subEditor.ReplaceNode(statement, newStatement); foreach (var match in matches) { - subEditor.RemoveNode(match.Statement); + subEditor.RemoveNode(match.Statement, SyntaxRemoveOptions.KeepUnbalancedDirectives); } document = document.WithSyntaxRoot(subEditor.GetChangedRoot()); From 3ae0bc44a62fe54d12dc37a59498987a9e9338bc Mon Sep 17 00:00:00 2001 From: Srivatsn Narayanan Date: Thu, 4 May 2017 14:12:43 -0700 Subject: [PATCH 146/214] Fix configurations so that projects can recognize x64 with 15.3 builds --- src/Compilers/Server/PortableServer/PortableServer.csproj | 1 + src/Interactive/CsiCore/CsiCore.csproj | 1 + src/Interactive/VbiCore/VbiCore.vbproj | 1 + .../DeployCoreClrTestRuntime/DeployCoreClrTestRuntime.csproj | 1 + .../DeployCompilerGeneratorToolsRuntime.csproj | 1 + .../Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj | 1 + .../CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj | 1 + .../Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj | 1 + .../VisualBasicErrorFactsGenerator.vbproj | 1 + .../VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj | 1 + 10 files changed, 10 insertions(+) diff --git a/src/Compilers/Server/PortableServer/PortableServer.csproj b/src/Compilers/Server/PortableServer/PortableServer.csproj index db879d1a28bd9..ff030d728141b 100644 --- a/src/Compilers/Server/PortableServer/PortableServer.csproj +++ b/src/Compilers/Server/PortableServer/PortableServer.csproj @@ -5,6 +5,7 @@ x64 AnyCPU + x64 x64 {06B26DCB-7A12-48EF-AE50-708593ABD05F} Exe diff --git a/src/Interactive/CsiCore/CsiCore.csproj b/src/Interactive/CsiCore/CsiCore.csproj index 404bc41443a34..f5ee930ed0275 100644 --- a/src/Interactive/CsiCore/CsiCore.csproj +++ b/src/Interactive/CsiCore/CsiCore.csproj @@ -5,6 +5,7 @@ x64 x64 + x64 {D1B051A4-F2A1-4E97-9747-C41D13E475FD} Exe CSharpInteractive diff --git a/src/Interactive/VbiCore/VbiCore.vbproj b/src/Interactive/VbiCore/VbiCore.vbproj index 6bfe4b00dbc83..1173ab452732c 100644 --- a/src/Interactive/VbiCore/VbiCore.vbproj +++ b/src/Interactive/VbiCore/VbiCore.vbproj @@ -5,6 +5,7 @@ x64 x64 + x64 {1EEFB4B6-A6CC-4869-AF05-A43C8B82A8FD} Exe Sub Main diff --git a/src/Test/DeployCoreClrTestRuntime/DeployCoreClrTestRuntime.csproj b/src/Test/DeployCoreClrTestRuntime/DeployCoreClrTestRuntime.csproj index 0f4381cf8a54a..4fa1f2cccaab2 100644 --- a/src/Test/DeployCoreClrTestRuntime/DeployCoreClrTestRuntime.csproj +++ b/src/Test/DeployCoreClrTestRuntime/DeployCoreClrTestRuntime.csproj @@ -5,6 +5,7 @@ x64 x64 + x64 Exe DeployCoreClrTestRuntime_DoNotUse false diff --git a/src/Tools/Source/CompilerGeneratorTools/DeployCompilerGeneratorToolsRuntime/DeployCompilerGeneratorToolsRuntime.csproj b/src/Tools/Source/CompilerGeneratorTools/DeployCompilerGeneratorToolsRuntime/DeployCompilerGeneratorToolsRuntime.csproj index 6c1d5daffb4dd..a7cfaf5197f2d 100644 --- a/src/Tools/Source/CompilerGeneratorTools/DeployCompilerGeneratorToolsRuntime/DeployCompilerGeneratorToolsRuntime.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/DeployCompilerGeneratorToolsRuntime/DeployCompilerGeneratorToolsRuntime.csproj @@ -6,6 +6,7 @@ 14.0 x64 x64 + x64 {6DA08F12-32F2-4DD9-BBAD-982EB71A2C9B} Exe DeployCompilerGeneratorToolsRuntime diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj index 105962d92b265..4211db95c80a7 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/BoundTreeGenerator/CompilersBoundTreeGenerator.csproj @@ -6,6 +6,7 @@ True x64 x64 + x64 {02459936-CD2C-4F61-B671-5C518F2A3DDC} Exe Roslyn.Compilers.Internal.BoundTreeGenerator diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj index d588982d9cd69..17fbabdc69905 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpErrorFactsGenerator/CSharpErrorFactsGenerator.csproj @@ -5,6 +5,7 @@ x64 x64 + x64 {288089C5-8721-458E-BE3E-78990DAB5E2E} Exe Roslyn.Compilers.CSharp.Internal.CSharpErrorFactsGenerator diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj index de57ab661a9e1..8f8c7a2918ca2 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/CSharpSyntaxGenerator/CSharpSyntaxGenerator.csproj @@ -5,6 +5,7 @@ x64 x64 + x64 {288089C5-8721-458E-BE3E-78990DAB5E2D} Exe Roslyn.Compilers.CSharp.Internal.CSharpSyntaxGenerator diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj index 4f2dc9a260d1b..01303056087b6 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj @@ -6,6 +6,7 @@ true x64 x64 + x64 {909B656F-6095-4AC2-A5AB-C3F032315C45} Exe diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj index 289f1a0d23404..fd78b5813f6f5 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj @@ -6,6 +6,7 @@ true x64 x64 + x64 {6AA96934-D6B7-4CC8-990D-DB6B9DD56E34} Exe Microsoft.CodeAnalysis.VisualBasic.Internal.VBSyntaxGenerator.Program From cd0e5e852db4cc5bccc2c376deec9b64e99b6085 Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 27 Apr 2017 12:17:00 -0700 Subject: [PATCH 147/214] Catch and report FileNotFoundException loading compiler assemblies --- .../Test/CommandLine/CommandLineTests.cs | 29 ++++++++-- src/Compilers/CSharp/csc/Program.cs | 21 +++++-- .../Core/CommandLine/BuildProtocol.cs | 6 +- .../Portable/CommandLine/CommonCompiler.cs | 2 +- .../Server/VBCSCompiler/VBCSCompiler.cs | 15 ++++- .../VBCSCompilerTests/CompilerServerTests.cs | 57 +++++++++++++------ .../Server/VBCSCompilerTests/ServerUtil.cs | 3 - src/Compilers/Shared/Csc.cs | 3 - src/Compilers/Shared/DesktopBuildClient.cs | 13 +---- .../Test/CommandLine/CommandLineTests.vb | 23 ++++++++ src/Compilers/VisualBasic/vbc/Program.cs | 21 +++++-- src/Test/Utilities/Desktop/AppDomainUtils.cs | 2 +- 12 files changed, 141 insertions(+), 54 deletions(-) diff --git a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs index e6cdafd4fd75d..b63e0120146af 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs +++ b/src/Compilers/CSharp/Test/CommandLine/CommandLineTests.cs @@ -1,14 +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; -using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; -using System.IO.MemoryMappedFiles; using System.Linq; using System.Reflection; using System.Reflection.Metadata; @@ -20,7 +18,6 @@ using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; -using Microsoft.CodeAnalysis.Debugging; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.Test.Utilities; @@ -9057,7 +9054,31 @@ public void CompilingCodeWithMultipleInvalidPreProcessorSymbolsShouldErrorOut() // warning CS2029: Invalid value for '/define'; '5' is not a valid identifier Diagnostic(ErrorCode.WRN_DefineIdentifierRequired).WithArguments("5")); } - + + [WorkItem(406649, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=406649")] + [ConditionalFact(typeof(IsEnglishLocal))] + public void MissingCompilerAssembly() + { + var dir = Temp.CreateDirectory(); + var cscPath = dir.CopyFile(typeof(Csc).Assembly.Location).Path; + + // Missing Microsoft.CodeAnalysis.CSharp.dll. + var result = ProcessUtilities.Run(cscPath, arguments: "/nologo /t:library unknown.cs", workingDirectory: dir.Path); + Assert.Equal(1, result.ExitCode); + Assert.Equal( + $"Could not load file or assembly '{typeof(CSharpCompilation).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.", + result.Output.Trim()); + + // Missing System.Collections.Immutable.dll. + dir.CopyFile(typeof(Compilation).Assembly.Location); + dir.CopyFile(typeof(CSharpCompilation).Assembly.Location); + result = ProcessUtilities.Run(cscPath, arguments: "/nologo /t:library unknown.cs", workingDirectory: dir.Path); + Assert.Equal(1, result.ExitCode); + Assert.Equal( + $"Could not load file or assembly '{typeof(ImmutableArray).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.", + result.Output.Trim()); + } + public class QuotedArgumentTests { private void VerifyQuotedValid(string name, string value, T expected, Func getValue) diff --git a/src/Compilers/CSharp/csc/Program.cs b/src/Compilers/CSharp/csc/Program.cs index 929d1b73223f0..c77a77d077931 100644 --- a/src/Compilers/CSharp/csc/Program.cs +++ b/src/Compilers/CSharp/csc/Program.cs @@ -1,19 +1,30 @@ // 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.IO; using Microsoft.CodeAnalysis.CommandLine; -using Roslyn.Utilities; -using System; namespace Microsoft.CodeAnalysis.CSharp.CommandLine { public class Program { public static int Main(string[] args) - => Main(args, Array.Empty()); + { + try + { + return MainCore(args); + } + catch (FileNotFoundException e) + { + // Catch exception from missing compiler assembly. + // Report the exception message and terminate the process. + Console.WriteLine(e.Message); + return CommonCompiler.Failed; + } + } - public static int Main(string[] args, string[] extraArgs) - => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.CSharpCompile, Csc.Run, new DesktopAnalyzerAssemblyLoader()); + private static int MainCore(string[] args) + => DesktopBuildClient.Run(args, RequestLanguage.CSharpCompile, Csc.Run, new DesktopAnalyzerAssemblyLoader()); public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, string tempDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader) => Csc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir, tempDir: tempDir), textWriter, analyzerLoader); diff --git a/src/Compilers/Core/CommandLine/BuildProtocol.cs b/src/Compilers/Core/CommandLine/BuildProtocol.cs index d41f0a30c3d79..b5124350822e7 100644 --- a/src/Compilers/Core/CommandLine/BuildProtocol.cs +++ b/src/Compilers/Core/CommandLine/BuildProtocol.cs @@ -128,14 +128,14 @@ public static async Task ReadAsync(Stream inStream, CancellationTo cancellationToken.ThrowIfCancellationRequested(); // Read the full request - var responseBuffer = new byte[length]; - await ReadAllAsync(inStream, responseBuffer, length, cancellationToken).ConfigureAwait(false); + var requestBuffer = new byte[length]; + await ReadAllAsync(inStream, requestBuffer, length, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); Log("Parsing request"); // Parse the request into the Request data structure. - using (var reader = new BinaryReader(new MemoryStream(responseBuffer), Encoding.Unicode)) + using (var reader = new BinaryReader(new MemoryStream(requestBuffer), Encoding.Unicode)) { var protocolVersion = reader.ReadUInt32(); var language = (RequestLanguage)reader.ReadUInt32(); diff --git a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs index 8297fe9c10c34..3f8bc693d892a 100644 --- a/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs +++ b/src/Compilers/Core/Portable/CommandLine/CommonCompiler.cs @@ -508,7 +508,7 @@ public StreamErrorLogger GetErrorLogger(TextWriter consoleOutput, CancellationTo } } - internal int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, CancellationToken cancellationToken) + private int RunCore(TextWriter consoleOutput, ErrorLogger errorLogger, CancellationToken cancellationToken) { Debug.Assert(!Arguments.IsScriptRunner); diff --git a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs index e5a6edc1d53cb..9d16d5a9ec963 100644 --- a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs +++ b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Specialized; using System.Configuration; +using System.IO; namespace Microsoft.CodeAnalysis.CompilerServer { @@ -25,8 +26,18 @@ public static int Main(string[] args) CompilerServerLogger.LogException(ex, "Error loading application settings"); } - var controller = new DesktopBuildServerController(appSettings); - return controller.Run(args); + try + { + var controller = new DesktopBuildServerController(appSettings); + return controller.Run(args); + } + catch (TypeInitializationException ex) when (ex.InnerException is FileNotFoundException) + { + // Assume FileNotFoundException was the result of a missing + // compiler assembly. Log the exception and terminate the process. + CompilerServerLogger.LogException(ex, "File not found"); + return CommonCompiler.Failed; + } } } } diff --git a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs index db661167e972a..255e90c9b2935 100644 --- a/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs +++ b/src/Compilers/Server/VBCSCompilerTests/CompilerServerTests.cs @@ -3,20 +3,15 @@ using System.Collections.Generic; using System.Diagnostics; using System.IO; -using System.IO.Pipes; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; -using System.Text; using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CommandLine; using Microsoft.CodeAnalysis.Test.Utilities; -using Microsoft.Win32; using Roslyn.Test.Utilities; using Xunit; -using System.Xml; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.CommandLine; -using Moq; namespace Microsoft.CodeAnalysis.CompilerServer.UnitTests { @@ -112,7 +107,7 @@ public CompilerServerUnitTests() #region Helpers - private IEnumerable> AddForLoggingEnvironmentVars(IEnumerable> vars) + private static IEnumerable> AddForLoggingEnvironmentVars(IEnumerable> vars) { vars = vars ?? new KeyValuePair[] { }; if (!vars.Where(kvp => kvp.Key == "RoslynCommandLineLogFile").Any()) @@ -162,13 +157,7 @@ private static void CheckForBadShared(string arguments) } } - public Process StartProcess(string fileName, string arguments, string workingDirectory = null) - { - CheckForBadShared(arguments); - return ProcessUtilities.StartProcess(fileName, arguments, workingDirectory); - } - - private ProcessResult RunCommandLineCompiler( + private static ProcessResult RunCommandLineCompiler( string compilerPath, string arguments, string currentDirectory, @@ -182,7 +171,7 @@ private ProcessResult RunCommandLineCompiler( additionalEnvironmentVars: AddForLoggingEnvironmentVars(additionalEnvironmentVars)); } - private ProcessResult RunCommandLineCompiler( + private static ProcessResult RunCommandLineCompiler( string compilerPath, string arguments, TempDirectory currentDirectory, @@ -1408,5 +1397,41 @@ public async Task Bug1024619_02() await serverData.Verify(connections: 2, completed: 2).ConfigureAwait(true); } } + + [WorkItem(406649, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=406649")] + [Fact] + public void MissingCompilerAssembly_CompilerServer() + { + var dir = Temp.CreateDirectory(); + var workingDirectory = dir.Path; + var serverExe = dir.CopyFile(CompilerServerExecutable).Path; + dir.CopyFile(typeof(System.Collections.Immutable.ImmutableArray).Assembly.Location); + + // Missing Microsoft.CodeAnalysis.dll launching server. + var result = ProcessUtilities.Run(serverExe, arguments: $"-pipename:{GetUniqueName()}", workingDirectory: workingDirectory); + Assert.Equal(1, result.ExitCode); + // Exception is logged rather than written to output/error streams. + Assert.Equal("", result.Output.Trim()); + } + + [WorkItem(406649, "https://devdiv.visualstudio.com/DevDiv/_workitems?id=406649")] + [WorkItem(19213, "https://github.com/dotnet/roslyn/issues/19213")] + [Fact(Skip = "19213")] + public async Task MissingCompilerAssembly_CompilerServerHost() + { + var host = new TestableCompilerServerHost((request, cancellationToken) => + { + throw new FileNotFoundException(); + }); + using (var serverData = ServerUtil.CreateServer(compilerServerHost: host)) + { + var request = new BuildRequest(1, RequestLanguage.CSharpCompile, new BuildRequest.Argument[0]); + var compileTask = ServerUtil.Send(serverData.PipeName, request); + var response = await compileTask; + Assert.Equal(BuildResponse.ResponseType.Completed, response.Type); + Assert.Equal(0, ((CompletedBuildResponse)response).ReturnCode); + await serverData.Verify(connections: 1, completed: 1).ConfigureAwait(true); + } + } } } diff --git a/src/Compilers/Server/VBCSCompilerTests/ServerUtil.cs b/src/Compilers/Server/VBCSCompilerTests/ServerUtil.cs index 11f0ee97bed0f..308e2ca434026 100644 --- a/src/Compilers/Server/VBCSCompilerTests/ServerUtil.cs +++ b/src/Compilers/Server/VBCSCompilerTests/ServerUtil.cs @@ -5,12 +5,9 @@ using Microsoft.CodeAnalysis.CommandLine; using System; -using System.Collections.Generic; using System.IO; using System.IO.Pipes; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; using System.Threading; using System.Threading.Tasks; using Moq; diff --git a/src/Compilers/Shared/Csc.cs b/src/Compilers/Shared/Csc.cs index 4d1eda1080212..592e3500f0b0d 100644 --- a/src/Compilers/Shared/Csc.cs +++ b/src/Compilers/Shared/Csc.cs @@ -3,9 +3,6 @@ using System; using System.IO; using System.Linq; -using System.Reflection; -using System.Text; -using Roslyn.Utilities; using Microsoft.CodeAnalysis.CommandLine; namespace Microsoft.CodeAnalysis.CSharp.CommandLine diff --git a/src/Compilers/Shared/DesktopBuildClient.cs b/src/Compilers/Shared/DesktopBuildClient.cs index d327aa368b85b..66acead90b2fc 100644 --- a/src/Compilers/Shared/DesktopBuildClient.cs +++ b/src/Compilers/Shared/DesktopBuildClient.cs @@ -2,20 +2,11 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.IO; using System.Linq; -using System.IO.Pipes; -using System.Runtime.InteropServices; -using System.Security.Principal; -using System.Text; using System.Threading; using System.Threading.Tasks; -using static Microsoft.CodeAnalysis.CommandLine.CompilerServerLogger; -using static Microsoft.CodeAnalysis.CommandLine.NativeMethods; using System.Reflection; -using System.Security.AccessControl; -using System.Security.Cryptography; namespace Microsoft.CodeAnalysis.CommandLine { @@ -37,7 +28,7 @@ internal DesktopBuildClient(RequestLanguage language, CompileFunc compileFunc, I _analyzerAssemblyLoader = analyzerAssemblyLoader; } - internal static int Run(IEnumerable arguments, IEnumerable extraArguments, RequestLanguage language, CompileFunc compileFunc, IAnalyzerAssemblyLoader analyzerAssemblyLoader) + internal static int Run(IEnumerable arguments, RequestLanguage language, CompileFunc compileFunc, IAnalyzerAssemblyLoader analyzerAssemblyLoader) { var client = new DesktopBuildClient(language, compileFunc, analyzerAssemblyLoader); var clientDir = AppContext.BaseDirectory; @@ -45,7 +36,7 @@ internal static int Run(IEnumerable arguments, IEnumerable extra var workingDir = Directory.GetCurrentDirectory(); var tempDir = BuildServerConnection.GetTempPath(workingDir); var buildPaths = new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir, tempDir: tempDir); - var originalArguments = BuildClient.GetCommandLineArgs(arguments).Concat(extraArguments).ToArray(); + var originalArguments = GetCommandLineArgs(arguments).ToArray(); return client.RunCompilation(originalArguments, buildPaths).ExitCode; } diff --git a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb index f2fce5f8fc94a..84f612cb5eb24 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb +++ b/src/Compilers/VisualBasic/Test/CommandLine/CommandLineTests.vb @@ -8290,6 +8290,29 @@ End Module parsedArgs.Errors.Verify(Diagnostic(ERRID.ERR_InvalidSwitchValue).WithArguments("langversion", "1000").WithLocation(1, 1)) End Sub + + + Public Sub MissingCompilerAssembly() + Dim dir = Temp.CreateDirectory() + Dim vbcPath = dir.CopyFile(GetType(Vbc).Assembly.Location).Path + + ' Missing Microsoft.CodeAnalysis.VisualBasic.dll. + Dim result = ProcessUtilities.Run(vbcPath, arguments:="/nologo /t:library unknown.vb", workingDirectory:=dir.Path) + Assert.Equal(1, result.ExitCode) + Assert.Equal( + $"Could not load file or assembly '{GetType(VisualBasicCompilation).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.", + result.Output.Trim()) + + ' Missing System.Collections.Immutable.dll. + dir.CopyFile(GetType(Compilation).Assembly.Location) + dir.CopyFile(GetType(VisualBasicCompilation).Assembly.Location) + result = ProcessUtilities.Run(vbcPath, arguments:="/nologo /t:library unknown.vb", workingDirectory:=dir.Path) + Assert.Equal(1, result.ExitCode) + Assert.Equal( + $"Could not load file or assembly '{GetType(ImmutableArray).Assembly.FullName}' or one of its dependencies. The system cannot find the file specified.", + result.Output.Trim()) + End Sub + Private Function MakeTrivialExe(Optional directory As String = Nothing) As String Return Temp.CreateFile(directory:=directory, prefix:="", extension:=".vb").WriteAllText(" Class Program diff --git a/src/Compilers/VisualBasic/vbc/Program.cs b/src/Compilers/VisualBasic/vbc/Program.cs index 0422f7a3c2079..7aef6f564b99a 100644 --- a/src/Compilers/VisualBasic/vbc/Program.cs +++ b/src/Compilers/VisualBasic/vbc/Program.cs @@ -1,19 +1,30 @@ // 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.IO; using Microsoft.CodeAnalysis.CommandLine; -using Roslyn.Utilities; -using System; namespace Microsoft.CodeAnalysis.VisualBasic.CommandLine { public class Program { public static int Main(string[] args) - => Main(args, Array.Empty()); + { + try + { + return MainCore(args); + } + catch (FileNotFoundException e) + { + // Catch exception from missing compiler assembly. + // Report the exception message and terminate the process. + Console.WriteLine(e.Message); + return CommonCompiler.Failed; + } + } - public static int Main(string[] args, string[] extraArgs) - => DesktopBuildClient.Run(args, extraArgs, RequestLanguage.VisualBasicCompile, Vbc.Run, new DesktopAnalyzerAssemblyLoader()); + private static int MainCore(string[] args) + => DesktopBuildClient.Run(args, RequestLanguage.VisualBasicCompile, Vbc.Run, new DesktopAnalyzerAssemblyLoader()); public static int Run(string[] args, string clientDir, string workingDir, string sdkDir, string tempDir, TextWriter textWriter, IAnalyzerAssemblyLoader analyzerLoader) => Vbc.Run(args, new BuildPaths(clientDir: clientDir, workingDir: workingDir, sdkDir: sdkDir, tempDir: tempDir), textWriter, analyzerLoader); diff --git a/src/Test/Utilities/Desktop/AppDomainUtils.cs b/src/Test/Utilities/Desktop/AppDomainUtils.cs index fca76da648b55..141d5997cb3c2 100644 --- a/src/Test/Utilities/Desktop/AppDomainUtils.cs +++ b/src/Test/Utilities/Desktop/AppDomainUtils.cs @@ -13,7 +13,7 @@ public static class AppDomainUtils public static AppDomain Create(string name = null, string basePath = null) { - name = name ?? "Custtom AppDomain"; + name = name ?? "Custom AppDomain"; basePath = basePath ?? Path.GetDirectoryName(typeof(AppDomainUtils).Assembly.Location); lock (s_lock) From e8f0a191bc45fbe7c949338b6227af5f7ed8bece Mon Sep 17 00:00:00 2001 From: Charles Stoner Date: Thu, 4 May 2017 11:03:10 -0700 Subject: [PATCH 148/214] Fix test --- .../Server/VBCSCompiler/VBCSCompiler.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs index 9d16d5a9ec963..b572e13c26d37 100644 --- a/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs +++ b/src/Compilers/Server/VBCSCompiler/VBCSCompiler.cs @@ -31,13 +31,22 @@ public static int Main(string[] args) var controller = new DesktopBuildServerController(appSettings); return controller.Run(args); } - catch (TypeInitializationException ex) when (ex.InnerException is FileNotFoundException) + catch (FileNotFoundException e) { - // Assume FileNotFoundException was the result of a missing - // compiler assembly. Log the exception and terminate the process. - CompilerServerLogger.LogException(ex, "File not found"); - return CommonCompiler.Failed; + // Assume the exception was the result of a missing compiler assembly. + LogException(e); } + catch (TypeInitializationException e) when (e.InnerException is FileNotFoundException) + { + // Assume the exception was the result of a missing compiler assembly. + LogException((FileNotFoundException)e.InnerException); + } + return CommonCompiler.Failed; + } + + private static void LogException(FileNotFoundException e) + { + CompilerServerLogger.LogException(e, "File not found"); } } } From 243ff236b045a27cf9ed75ee70c33f771a8d08d4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Thu, 4 May 2017 07:51:41 -0500 Subject: [PATCH 149/214] Use a wait-free fast path in AsyncLazy.GetValueAsync and GetValue --- .../Core/Portable/Utilities/AsyncLazy`1.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Utilities/AsyncLazy`1.cs b/src/Workspaces/Core/Portable/Utilities/AsyncLazy`1.cs index 53f8ca15a3ad1..a443cd6985437 100644 --- a/src/Workspaces/Core/Portable/Utilities/AsyncLazy`1.cs +++ b/src/Workspaces/Core/Portable/Utilities/AsyncLazy`1.cs @@ -179,6 +179,12 @@ public override T GetValue(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + // If the value is already available, return it immediately + if (TryGetValue(out T value)) + { + return value; + } + Request request = null; AsynchronousComputationToStart? newAsynchronousComputation = null; @@ -297,7 +303,14 @@ public override Task GetValueAsync(CancellationToken cancellationToken) // Optimization: if we're already cancelled, do not pass go if (cancellationToken.IsCancellationRequested) { - return new Task(() => default(T), cancellationToken); + return Task.FromCanceled(cancellationToken); + } + + // Avoid taking the lock if a cached value is available + var cachedResult = _cachedResult; + if (cachedResult != null) + { + return cachedResult; } Request request; From 859a43e7b6b03642a461302f49e6bbb9a06b9964 Mon Sep 17 00:00:00 2001 From: Srivatsn Narayanan Date: Thu, 4 May 2017 15:50:15 -0700 Subject: [PATCH 150/214] Fix configuratiosn so that project will load in 15.3 --- src/Compilers/Server/PortableServer/PortableServer.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/Server/PortableServer/PortableServer.csproj b/src/Compilers/Server/PortableServer/PortableServer.csproj index ff030d728141b..90cf76414e3da 100644 --- a/src/Compilers/Server/PortableServer/PortableServer.csproj +++ b/src/Compilers/Server/PortableServer/PortableServer.csproj @@ -5,7 +5,7 @@ x64 AnyCPU - x64 + AnyCPU;x64 x64 {06B26DCB-7A12-48EF-AE50-708593ABD05F} Exe From 91495074548e1c2aae88610eb1550a04261794d2 Mon Sep 17 00:00:00 2001 From: Roger Date: Wed, 19 Apr 2017 11:16:57 -0400 Subject: [PATCH 151/214] Mac OS X is now macOS (README.md) This PR updates all mentions of OSX/OS X to macOS in the README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 09374138191bd..192a511459271 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ |**dev16**|[![Build Status](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_debug_unit32/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_debug_unit32/)|[![Build Status](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_debug_unit64/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_debug_unit64/)|[![Build Status](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_release_unit32/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_release_unit32/)|[![Build Status](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_release_unit64/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_release_unit64/)|[![Build Status](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_determinism/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_determinism/)|[![Build Status](https://ci.dot.net/buildStatus/icon?job=dotnet_roslyn/dev16/windows_debug_vs-integration)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_debug_vs-integration/)|[![Build Status](https://ci.dot.net/buildStatus/icon?job=dotnet_roslyn/dev16/windows_release_vs-integration)](https://ci.dot.net/job/dotnet_roslyn/job/dev16/job/windows_release_vs-integration/)| ### Linux/Mac - Unit Tests -|Branch|Ubuntu14|Ubuntu16|MacOSX| +|Branch|Ubuntu14|Ubuntu16|macOS| |:--:|:--:|:--:|:--:| |**master**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_14_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_14_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_16_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/ubuntu_16_debug/)|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/master/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/master/job/mac_debug/)| |**dev15.0.x**|[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/linux_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/linux_debug/)||[![BuildStatus](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/mac_debug/badge/icon)](https://ci.dot.net/job/dotnet_roslyn/job/dev15.0.x/job/mac_debug/)| From 60a63eb61c33dc5db38ea1aed88c91eea1c41b4d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 17:11:30 -0700 Subject: [PATCH 152/214] Swallow very specific exception messages. --- .../PackageInstallerServiceFactory.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index 261e8c760b05f..e1de1f865368e 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -286,10 +286,26 @@ private string GetInstalledVersion(string packageName, EnvDTE.Project dteProject var metadata = installedPackages.FirstOrDefault(m => m.Id == packageName); return metadata?.VersionString; } + catch (ArgumentException e) when (IsKnownNugetIssue(e)) + { + // Nuget may throw an ArgumentException when there is something about the project + // they do not like/support. + Console.WriteLine(e); + } catch (Exception e) when (FatalError.ReportWithoutCrash(e)) { - return null; } + + return null; + } + + private bool IsKnownNugetIssue(ArgumentException exception) + { + // See https://github.com/NuGet/Home/issues/4706 + // Nuget throws on legal projects. We do not want to report this exception + // as it is known (and NFWs are expensive), but we do want to report if we + // run into anything else. + return exception.Message.Contains("is not a valid version string"); } private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) @@ -443,7 +459,7 @@ private void ProcessProjectChange(Solution solution, ProjectId projectId) installedPackages.AddRange(installedPackageMetadata.Select(m => new KeyValuePair(m.Id, m.VersionString))); isEnabled = true; } - catch (ArgumentException) + catch (ArgumentException e) when (IsKnownNugetIssue(e)) { // Nuget may throw an ArgumentException when there is something about the project // they do not like/support. From cc4852d86696327fb15cfe3a742248b91fe652f7 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 17:11:53 -0700 Subject: [PATCH 153/214] Remove WriteLine. --- .../Core/Def/Packaging/PackageInstallerServiceFactory.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs index e1de1f865368e..2731b73f54f7d 100644 --- a/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Packaging/PackageInstallerServiceFactory.cs @@ -290,7 +290,6 @@ private string GetInstalledVersion(string packageName, EnvDTE.Project dteProject { // Nuget may throw an ArgumentException when there is something about the project // they do not like/support. - Console.WriteLine(e); } catch (Exception e) when (FatalError.ReportWithoutCrash(e)) { From 366fc20bdd15d0b36de98bfbccb930747a1c8735 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 18:43:04 -0700 Subject: [PATCH 154/214] Better handling of directive-trivia in UseExpressionBody/ReplacePropWithMethod. --- .../ReplacePropertyWithMethodsTests.cs | 114 ++++++++++++++++++ ...xpressionBodyForPropertiesAnalyzerTests.cs | 98 +++++++++++++++ .../CSharpReplaceMethodWithPropertyService.cs | 29 +++-- ...CSharpReplacePropertyWithMethodsService.cs | 14 ++- .../Helpers/UseExpressionBodyHelper`1.cs | 23 ++-- .../ArrowExpressionClauseSyntaxExtensions.cs | 15 ++- .../Extensions/BlockSyntaxExtensions.cs | 4 +- 7 files changed, 264 insertions(+), 33 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs index 2fe206b531079..0bcebe509cef2 100644 --- a/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs +++ b/src/EditorFeatures/CSharpTest/CodeActions/ReplacePropertyWithMethods/ReplacePropertyWithMethodsTests.cs @@ -1416,6 +1416,120 @@ object [||]ActiveProjectContext }", ignoreTrivia: false); } + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestWithDirectives1() + { + await TestInRegularAndScriptAsync( +@"class C +{ + int [||]Prop + { + get + { +#if true + return 0; +#else + return 1; +#endif + } + } +}", + @"class C +{ + private int GetProp() + { +#if true + return 0; +#else + return 1; +#endif + } +}", ignoreTrivia: false); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestWithDirectives2() + { + await TestInRegularAndScriptAsync( +@"class C +{ + int [||]Prop + { + get + { +#if true + return 0; +#else + return 1; +#endif + } + } +}", + @"class C +{ + private int GetProp() => +#if true + 0; +#else + return 1; +#endif +}", ignoreTrivia: false, + options: PreferExpressionBodiedMethods); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestWithDirectives3() + { + await TestInRegularAndScriptAsync( +@"class C +{ + int [||]Prop => +#if true + 0; +#else + 1; +#endif +}", +@"class C +{ + private int GetProp() => +#if true + 0; +#else + 1; +#endif +}", ignoreTrivia: false); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)] + public async Task TestWithDirectives4() + { + await TestInRegularAndScriptAsync( +@"class C +{ + int [||]Prop => +#if true + 0; +#else + 1; +#endif +}", +@"class C +{ + private int GetProp() => +#if true + 0; +#else + 1; +#endif +}", ignoreTrivia: false, + options: PreferExpressionBodiedMethods); + } + private IDictionary PreferExpressionBodiedMethods => OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement)); } diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs index 59bee66a86ebd..abab2cd8c4ee7 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs @@ -244,5 +244,103 @@ await TestInRegularAndScriptAsync( public string OtherThing => ""Pickles""; }", ignoreTrivia: false, options: UseExpressionBody); } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestDirectivesInBlockBody1() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { +#if true + [|return|] Bar(); +#else + return Baz(); +#endif + } + } +}", + +@"class C +{ + int Foo => +#if true + Bar(); +#else + return Baz(); +#endif + +}", ignoreTrivia: false, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestDirectivesInBlockBody2() + { + await TestInRegularAndScript1Async( +@"class C +{ + int Foo + { + get + { +#if false + return Bar(); +#else + [|return|] Baz(); +#endif + } + } +}", + +@"class C +{ + int Foo => +#if false + return Bar(); +#else + Baz(); +#endif + +}", ignoreTrivia: false, + parameters: new TestParameters(options: UseExpressionBody)); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestMissingWithDirectivesInExpressionBody1() + { + await TestMissingInRegularAndScriptAsync( +@"class C +{ + int Foo [|=>|] +#if true + Bar(); +#else + Baz(); +#endif +}", parameters: new TestParameters(options: UseBlockBody)); + } + + [WorkItem(19235, "https://github.com/dotnet/roslyn/issues/19235")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestMissingWithDirectivesInExpressionBody2() + { + await TestMissingInRegularAndScriptAsync( +@"class C +{ + int Foo [|=>|] +#if false + Bar(); +#else + Baz(); +#endif +}", parameters: new TestParameters(options: UseBlockBody)); + } } } \ No newline at end of file diff --git a/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs b/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs index e170f83e29286..d2609f8f9c02f 100644 --- a/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs +++ b/src/Features/CSharp/Portable/ReplaceMethodWithProperty/CSharpReplaceMethodWithPropertyService.cs @@ -95,7 +95,7 @@ public SyntaxNode ConvertMethodsToProperty( } else if (getAccessor.Body != null && getAccessor.Body.TryConvertToExpressionBody( - parseOptions, expressionBodyPreference, + parseOptions, expressionBodyPreference, out var arrowExpression, out var semicolonToken)) { return propertyDeclaration.WithExpressionBody(arrowExpression) @@ -106,10 +106,12 @@ public SyntaxNode ConvertMethodsToProperty( } else { - if (propertyDeclaration.ExpressionBody != null) + if (propertyDeclaration.ExpressionBody != null && + propertyDeclaration.ExpressionBody.TryConvertToBlock( + propertyDeclaration.SemicolonToken, + createReturnStatementForExpression: true, + block: out var block)) { - var block = propertyDeclaration.ExpressionBody.ConvertToBlock( - propertyDeclaration.SemicolonToken, createReturnStatementForExpression: true); var accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithBody(block); @@ -181,7 +183,7 @@ private static AccessorDeclarationSyntax UseExpressionOrBlockBodyIfDesired( if (accessorDeclaration?.Body != null && expressionBodyPreference != ExpressionBodyPreference.Never) { if (accessorDeclaration.Body.TryConvertToExpressionBody( - parseOptions, expressionBodyPreference, + parseOptions, expressionBodyPreference, out var arrowExpression, out var semicolonToken)) { return accessorDeclaration.WithBody(null) @@ -192,13 +194,16 @@ private static AccessorDeclarationSyntax UseExpressionOrBlockBodyIfDesired( } else if (accessorDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never) { - var block = accessorDeclaration.ExpressionBody.ConvertToBlock( - accessorDeclaration.SemicolonToken, - createReturnStatementForExpression: accessorDeclaration.Kind() == SyntaxKind.GetAccessorDeclaration); - return accessorDeclaration.WithExpressionBody(null) - .WithSemicolonToken(default(SyntaxToken)) - .WithBody(block) - .WithAdditionalAnnotations(Formatter.Annotation); + if (accessorDeclaration.ExpressionBody.TryConvertToBlock( + accessorDeclaration.SemicolonToken, + createReturnStatementForExpression: accessorDeclaration.Kind() == SyntaxKind.GetAccessorDeclaration, + block: out var block)) + { + return accessorDeclaration.WithExpressionBody(null) + .WithSemicolonToken(default(SyntaxToken)) + .WithBody(block) + .WithAdditionalAnnotations(Formatter.Annotation); + } } return accessorDeclaration; diff --git a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs index f47268dd13f8c..5b8ed9d9522ee 100644 --- a/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs +++ b/src/Features/CSharp/Portable/ReplacePropertyWithMethods/CSharpReplacePropertyWithMethodsService.cs @@ -246,12 +246,14 @@ private static SyntaxNode UseExpressionOrBlockBodyIfDesired( } else if (methodDeclaration?.ExpressionBody != null && expressionBodyPreference == ExpressionBodyPreference.Never) { - var block = methodDeclaration?.ExpressionBody.ConvertToBlock( - methodDeclaration.SemicolonToken, createReturnStatementForExpression); - return methodDeclaration.WithExpressionBody(null) - .WithSemicolonToken(default(SyntaxToken)) - .WithBody(block) - .WithAdditionalAnnotations(Formatter.Annotation); + if (methodDeclaration.ExpressionBody.TryConvertToBlock( + methodDeclaration.SemicolonToken, createReturnStatementForExpression, out var block)) + { + return methodDeclaration.WithExpressionBody(null) + .WithSemicolonToken(default(SyntaxToken)) + .WithBody(block) + .WithAdditionalAnnotations(Formatter.Annotation); + } } return methodDeclaration; diff --git a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs index 0ffa11a3fe0ee..c1e823e78a338 100644 --- a/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs +++ b/src/Features/CSharp/Portable/UseExpressionBody/Helpers/UseExpressionBodyHelper`1.cs @@ -128,8 +128,8 @@ public virtual bool CanOfferUseBlockBody( // If the user does not like block bodies then we offer block bodies from the refactoring provider. if (userPrefersBlockBodies == forAnalyzer) { - // If we have an expression body, we can always convert it to a block body. - return this.GetExpressionBody(declaration) != null; + return this.GetExpressionBody(declaration)?.TryConvertToBlock( + SyntaxFactory.Token(SyntaxKind.SemicolonToken), false, out var block) == true; } return false; @@ -180,11 +180,16 @@ protected virtual TDeclaration WithGenerateBody( { var expressionBody = GetExpressionBody(declaration); var semicolonToken = GetSemicolonToken(declaration); - var block = expressionBody.ConvertToBlock( - GetSemicolonToken(declaration), - CreateReturnStatementForExpression(declaration)); - return WithBody(declaration, block); + if (expressionBody.TryConvertToBlock( + GetSemicolonToken(declaration), + CreateReturnStatementForExpression(declaration), + out var block)) + { + return WithBody(declaration, block); + } + + return declaration; } protected TDeclaration WithAccessorList( @@ -196,7 +201,8 @@ protected TDeclaration WithAccessorList( var expressionBodyPreference = options.GetOption(CSharpCodeStyleOptions.PreferExpressionBodiedAccessors).Value; AccessorDeclarationSyntax accessor; - if (expressionBodyPreference != ExpressionBodyPreference.Never) + if (expressionBodyPreference != ExpressionBodyPreference.Never || + !expressionBody.TryConvertToBlock(GetSemicolonToken(declaration), CreateReturnStatementForExpression(declaration), out var block)) { accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration) .WithExpressionBody(expressionBody) @@ -204,9 +210,6 @@ protected TDeclaration WithAccessorList( } else { - var block = expressionBody.ConvertToBlock( - GetSemicolonToken(declaration), - CreateReturnStatementForExpression(declaration)); accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.GetAccessorDeclaration, block); } diff --git a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs index 874495e4dd46d..89daf448fa5b7 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.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.Linq; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Shared.Extensions; @@ -7,14 +8,22 @@ namespace Microsoft.CodeAnalysis.CSharp.Extensions { internal static class ArrowExpressionClauseSyntaxExtensions { - public static BlockSyntax ConvertToBlock( + public static bool TryConvertToBlock( this ArrowExpressionClauseSyntax arrowExpression, SyntaxToken semicolonToken, - bool createReturnStatementForExpression) + bool createReturnStatementForExpression, + out BlockSyntax block) { + if (arrowExpression.Expression.GetLeadingTrivia().Any(t => t.IsDirective)) + { + block = null; + return false; + } + var statement = ConvertToStatement(arrowExpression.Expression, semicolonToken, createReturnStatementForExpression); statement = statement.WithPrependedLeadingTrivia(arrowExpression.ArrowToken.TrailingTrivia); - return SyntaxFactory.Block(statement); + block = SyntaxFactory.Block(statement); + return true; } private static StatementSyntax ConvertToStatement( diff --git a/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs index fa2d3b936d989..acb5a77d03d19 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/BlockSyntaxExtensions.cs @@ -70,9 +70,9 @@ private static bool TryGetExpression( { if (returnStatement.Expression != null) { - // If there are any comments on the return keyword, move them to + // If there are any comments or directives on the return keyword, move them to // the expression. - expression = firstStatement.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment()) + expression = firstStatement.GetLeadingTrivia().Any(t => t.IsDirective || t.IsSingleOrMultiLineComment()) ? returnStatement.Expression.WithLeadingTrivia(returnStatement.GetLeadingTrivia()) : returnStatement.Expression; semicolonToken = returnStatement.SemicolonToken; From 86c6c0479b14c578ee528d75e14a3f2d4b160aa5 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 18:47:31 -0700 Subject: [PATCH 155/214] Add comment. --- .../Extensions/ArrowExpressionClauseSyntaxExtensions.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs index 89daf448fa5b7..231d5c0eb5ace 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs @@ -14,6 +14,9 @@ public static bool TryConvertToBlock( bool createReturnStatementForExpression, out BlockSyntax block) { + // It's tricky to convert an arrow expression with directives over to a block. + // We'd need to find and remove the directives *after* the arrow expression and + // move them accordingly. So, for now, we just disallow this. if (arrowExpression.Expression.GetLeadingTrivia().Any(t => t.IsDirective)) { block = null; From b9a3e8254db1088abddf4c490d6ab72bf303dd6b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 19:38:30 -0700 Subject: [PATCH 156/214] Fix crash in AddUsing when changing casing of a typename. --- .../CSharpTest/AddUsing/AddUsingTests.cs | 49 +++++++++++++++++++ .../PackageReference.ParentCodeAction.cs | 3 +- .../AddImport/References/AssemblyReference.cs | 5 +- .../AddImport/References/Reference.cs | 30 ++++++------ .../AddImport/References/SymbolReference.cs | 17 ++++--- 5 files changed, 79 insertions(+), 25 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs index df00e9a350471..3c674d113aea5 100644 --- a/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs +++ b/src/EditorFeatures/CSharpTest/AddUsing/AddUsingTests.cs @@ -4554,5 +4554,54 @@ void M() } }"); } + + [WorkItem(19218, "https://github.com/dotnet/roslyn/issues/19218")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddImport)] + public async Task TestChangeCaseWithUsingsInNestedNamespace() + { + await TestInRegularAndScriptAsync( +@"namespace VS +{ + interface IVsStatusbar + { + } +} + +namespace Outer +{ + using System; + + class C + { + void M() + { + // Note: IVsStatusBar is cased incorrectly. + [|IVsStatusBar|] b; + } + } +} +", +@"namespace VS +{ + interface IVsStatusbar + { + } +} + +namespace Outer +{ + using System; + using VS; + + class C + { + void M() + { + IVsStatusbar b; + } + } +} +"); + } } } \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.ParentCodeAction.cs b/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.ParentCodeAction.cs index 87560df22c47d..f7265a2c46e4b 100644 --- a/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.ParentCodeAction.cs +++ b/src/Features/Core/Portable/AddImport/CodeActions/PackageReference.ParentCodeAction.cs @@ -107,7 +107,8 @@ private static async Task GetInstallDataAsync( CancellationToken cancellationToken) { var oldDocument = document; - reference.ReplaceNameNode(ref node, ref document, cancellationToken); + (node, document) = await reference.ReplaceNameNodeAsync( + node, document, cancellationToken).ConfigureAwait(false); var newDocument = await reference.provider.AddImportAsync( node, reference.SearchResult.NameParts, document, placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs b/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs index 07f8df2926288..ba59aac0fadb1 100644 --- a/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs +++ b/src/Features/Core/Portable/AddImport/References/AssemblyReference.cs @@ -105,9 +105,8 @@ protected override async Task> ComputeOperation var reference = service.GetReference(resolvedPath, MetadataReferenceProperties.Assembly); // First add the "using/import" directive in the code. - var node = _node; - var document = _document; - _reference.ReplaceNameNode(ref node, ref document, cancellationToken); + (SyntaxNode node, Document document) = await _reference.ReplaceNameNodeAsync( + _node, _document, cancellationToken).ConfigureAwait(false); var newDocument = await _reference.provider.AddImportAsync( node, _reference.SearchResult.NameParts, document, _placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/AddImport/References/Reference.cs b/src/Features/Core/Portable/AddImport/References/Reference.cs index a36eb4646d36c..a6e2515091352 100644 --- a/src/Features/Core/Portable/AddImport/References/Reference.cs +++ b/src/Features/Core/Portable/AddImport/References/Reference.cs @@ -92,26 +92,28 @@ public override int GetHashCode() return Hash.CombineValues(this.SearchResult.NameParts); } - protected void ReplaceNameNode( - ref SyntaxNode contextNode, ref Document document, CancellationToken cancellationToken) + protected async Task<(SyntaxNode, Document)> ReplaceNameNodeAsync( + SyntaxNode contextNode, Document document, CancellationToken cancellationToken) { - if (!this.SearchResult.DesiredNameDiffersFromSourceName()) + if (this.SearchResult.DesiredNameDiffersFromSourceName()) { - return; - } + var identifier = SearchResult.NameNode.GetFirstToken(); + var generator = SyntaxGenerator.GetGenerator(document); + var newIdentifier = generator.IdentifierName(SearchResult.DesiredName).GetFirstToken().WithTriviaFrom(identifier); + var annotation = new SyntaxAnnotation(); + + var root = contextNode.SyntaxTree.GetRoot(cancellationToken); + root = root.ReplaceToken(identifier, newIdentifier.WithAdditionalAnnotations(annotation)); + document = document.WithSyntaxRoot(root); - var identifier = SearchResult.NameNode.GetFirstToken(); - var generator = SyntaxGenerator.GetGenerator(document); - var newIdentifier = generator.IdentifierName(SearchResult.DesiredName).GetFirstToken().WithTriviaFrom(identifier); - var annotation = new SyntaxAnnotation(); + var currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + contextNode = currentRoot.GetAnnotatedTokens(annotation).First().Parent; + } - var root = contextNode.SyntaxTree.GetRoot(cancellationToken); - root = root.ReplaceToken(identifier, newIdentifier.WithAdditionalAnnotations(annotation)); - document = document.WithSyntaxRoot(root); - contextNode = root.GetAnnotatedTokens(annotation).First().Parent; + return (contextNode, document); } public abstract Task CreateCodeActionAsync(Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken); } } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs index 4ad54cbb54f05..9b27495bbc966 100644 --- a/src/Features/Core/Portable/AddImport/References/SymbolReference.cs +++ b/src/Features/Core/Portable/AddImport/References/SymbolReference.cs @@ -2,9 +2,11 @@ using System; using System.Collections.Immutable; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.FindSymbols; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CodeFixes.AddImport @@ -61,22 +63,23 @@ private async Task GetOperationAsync( protected virtual Solution GetUpdatedSolution(Document newDocument) => newDocument.Project.Solution; - private Task UpdateDocumentAsync( + private async Task UpdateDocumentAsync( Document document, SyntaxNode contextNode, bool placeSystemNamespaceFirst, bool hasExistingImport, CancellationToken cancellationToken) { - ReplaceNameNode(ref contextNode, ref document, cancellationToken); - // Defer to the language to add the actual import/using. if (hasExistingImport) { - return Task.FromResult(document); + return document; } - return provider.AddImportAsync(contextNode, - this.SymbolResult.Symbol, document, - placeSystemNamespaceFirst, cancellationToken); + (var newContextNode, var newDocument) = await ReplaceNameNodeAsync( + contextNode, document, cancellationToken).ConfigureAwait(false); + + return await provider.AddImportAsync( + newContextNode, this.SymbolResult.Symbol, newDocument, + placeSystemNamespaceFirst, cancellationToken).ConfigureAwait(false); } public override async Task CreateCodeActionAsync( From bfc2402b14704644089eeb0cda0202d60a43ed5d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 19:44:09 -0700 Subject: [PATCH 157/214] Make names clearer. --- .../AddImport/References/Reference.cs | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/Features/Core/Portable/AddImport/References/Reference.cs b/src/Features/Core/Portable/AddImport/References/Reference.cs index a6e2515091352..51f51030b7c03 100644 --- a/src/Features/Core/Portable/AddImport/References/Reference.cs +++ b/src/Features/Core/Portable/AddImport/References/Reference.cs @@ -95,22 +95,24 @@ public override int GetHashCode() protected async Task<(SyntaxNode, Document)> ReplaceNameNodeAsync( SyntaxNode contextNode, Document document, CancellationToken cancellationToken) { - if (this.SearchResult.DesiredNameDiffersFromSourceName()) + if (!this.SearchResult.DesiredNameDiffersFromSourceName()) { - var identifier = SearchResult.NameNode.GetFirstToken(); - var generator = SyntaxGenerator.GetGenerator(document); - var newIdentifier = generator.IdentifierName(SearchResult.DesiredName).GetFirstToken().WithTriviaFrom(identifier); - var annotation = new SyntaxAnnotation(); + return (contextNode, document); + } - var root = contextNode.SyntaxTree.GetRoot(cancellationToken); - root = root.ReplaceToken(identifier, newIdentifier.WithAdditionalAnnotations(annotation)); - document = document.WithSyntaxRoot(root); + var identifier = SearchResult.NameNode.GetFirstToken(); + var generator = SyntaxGenerator.GetGenerator(document); + var newIdentifier = generator.IdentifierName(SearchResult.DesiredName).GetFirstToken().WithTriviaFrom(identifier); + var annotation = new SyntaxAnnotation(); - var currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); - contextNode = currentRoot.GetAnnotatedTokens(annotation).First().Parent; - } + var root = contextNode.SyntaxTree.GetRoot(cancellationToken); + root = root.ReplaceToken(identifier, newIdentifier.WithAdditionalAnnotations(annotation)); + + var newDocument = document.WithSyntaxRoot(root); + var newRoot = await newDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + var newContextNode = newRoot.GetAnnotatedTokens(annotation).First().Parent; - return (contextNode, document); + return (newContextNode, newDocument); } public abstract Task CreateCodeActionAsync(Document document, SyntaxNode node, bool placeSystemNamespaceFirst, CancellationToken cancellationToken); From bf9366705e3043edcd2971ae22b0518b5c8b8e70 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 21:29:21 -0700 Subject: [PATCH 158/214] Preserve trivia better when converting from an expression body to a block body. --- ...xpressionBodyForPropertiesAnalyzerTests.cs | 22 +++++++++++++++++ .../ArrowExpressionClauseSyntaxExtensions.cs | 24 +++++++++++++++---- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs index abab2cd8c4ee7..d2e21eb196482 100644 --- a/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/UseExpressionBody/Analyzer/UseExpressionBodyForPropertiesAnalyzerTests.cs @@ -342,5 +342,27 @@ await TestMissingInRegularAndScriptAsync( #endif }", parameters: new TestParameters(options: UseBlockBody)); } + + [WorkItem(19193, "https://github.com/dotnet/roslyn/issues/19193")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExpressionBody)] + public async Task TestMoveTriviaFromExpressionToReturnStatement() + { + await TestInRegularAndScriptAsync( +@"class C +{ + int Foo(int i) [|=>|] + //comment + i * i; +}", +@"class C +{ + int Foo(int i) + { + //comment + return i * i; + } +}", ignoreTrivia: false, + options: UseBlockBody); + } } } \ No newline at end of file diff --git a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs index 231d5c0eb5ace..1162a5fdf57c1 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs @@ -23,9 +23,15 @@ public static bool TryConvertToBlock( return false; } + var openBrace = SyntaxFactory.Token(SyntaxKind.OpenBraceToken) + .WithTrailingTrivia(arrowExpression.ArrowToken.TrailingTrivia); + var statement = ConvertToStatement(arrowExpression.Expression, semicolonToken, createReturnStatementForExpression); - statement = statement.WithPrependedLeadingTrivia(arrowExpression.ArrowToken.TrailingTrivia); - block = SyntaxFactory.Block(statement); + + block = SyntaxFactory.Block( + openBrace, + SyntaxFactory.SingletonList(statement), + SyntaxFactory.Token(SyntaxKind.CloseBraceToken)); return true; } @@ -41,8 +47,18 @@ private static StatementSyntax ConvertToStatement( } else if (createReturnStatementForExpression) { - return SyntaxFactory.ReturnStatement(expression) - .WithSemicolonToken(semicolonToken); + if (expression.GetLeadingTrivia().Any(t => t.IsSingleOrMultiLineComment())) + { + return SyntaxFactory.ReturnStatement(expression.WithLeadingTrivia(SyntaxFactory.ElasticSpace)) + .WithSemicolonToken(semicolonToken) + .WithLeadingTrivia(expression.GetLeadingTrivia()) + .WithPrependedLeadingTrivia(SyntaxFactory.ElasticMarker); + } + else + { + return SyntaxFactory.ReturnStatement(expression) + .WithSemicolonToken(semicolonToken); + } } else { From ebc112baca89eabc6f41834f56e1258f2582cb83 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 21:49:38 -0700 Subject: [PATCH 159/214] Order 'Remove Unused Variable' after 'Add Using' --- .../RemoveUnusedVariable/RemoveUnusedVariableTest.cs | 2 +- .../RemoveUnusedVariable/RemoveUnusedVariableTest.vb | 2 +- src/Features/CSharp/Portable/CSharpFeatures.csproj | 2 +- ...der.cs => CSharpRemoveUnusedVariableCodeFixProvider.cs} | 3 ++- src/Features/VisualBasic/Portable/BasicFeatures.vbproj | 2 +- ...b => VisualBasicRemoveUnusedVariableCodeFixProvider.vb} | 7 ++++--- 6 files changed, 10 insertions(+), 8 deletions(-) rename src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/{RemoveUnusedVariableCodeFixProvider.cs => CSharpRemoveUnusedVariableCodeFixProvider.cs} (74%) rename src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/{RemoveUnusedVariableCodeFixProvider.vb => VisualBasicRemoveUnusedVariableCodeFixProvider.vb} (82%) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs b/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs index 3af44cb53b939..25a7aba5cc308 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs @@ -14,7 +14,7 @@ public partial class RemoveUnusedVariableTest : AbstractCSharpDiagnosticProvider { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) { - return(null, new RemoveUnusedVariableCodeFixProvider()); + return(null, new CSharpRemoveUnusedVariableCodeFixProvider()); } [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)] diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb index 582664dc59c2f..d2474f23d44ee 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb @@ -10,7 +10,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) Return (Nothing, - New RemoveUnusedVariableCodeFixProvider()) + New VisualBasicRemoveUnusedVariableCodeFixProvider()) End Function diff --git a/src/Features/CSharp/Portable/CSharpFeatures.csproj b/src/Features/CSharp/Portable/CSharpFeatures.csproj index a15b756b7d3c3..1dfc62d0c6efa 100644 --- a/src/Features/CSharp/Portable/CSharpFeatures.csproj +++ b/src/Features/CSharp/Portable/CSharpFeatures.csproj @@ -62,7 +62,7 @@ - + diff --git a/src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.cs b/src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs similarity index 74% rename from src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.cs rename to src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs index 9dddc12e4263b..6e20eff76d446 100644 --- a/src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/CodeFixes/RemoveUnusedVariable/CSharpRemoveUnusedVariableCodeFixProvider.cs @@ -9,7 +9,8 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeFixes.RemoveUnusedVariable { [ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.RemoveUnusedVariable), Shared] - internal partial class RemoveUnusedVariableCodeFixProvider : AbstractRemoveUnusedVariableCodeFixProvider + [ExtensionOrder(After = PredefinedCodeFixProviderNames.AddImport)] + internal partial class CSharpRemoveUnusedVariableCodeFixProvider : AbstractRemoveUnusedVariableCodeFixProvider { private const string CS0168 = nameof(CS0168); private const string CS0219 = nameof(CS0219); diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 71f6b35a7f052..6bfd88518e196 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -90,7 +90,7 @@ - + diff --git a/src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.vb b/src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/VisualBasicRemoveUnusedVariableCodeFixProvider.vb similarity index 82% rename from src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.vb rename to src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/VisualBasicRemoveUnusedVariableCodeFixProvider.vb index 88222e5e9e66a..41103342ca8fe 100644 --- a/src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/RemoveUnusedVariableCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/CodeFixes/RemoveUnusedVariable/VisualBasicRemoveUnusedVariableCodeFixProvider.vb @@ -8,8 +8,9 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnusedVariable - - Friend Class RemoveUnusedVariableCodeFixProvider + + + Friend Class VisualBasicRemoveUnusedVariableCodeFixProvider Inherits AbstractRemoveUnusedVariableCodeFixProvider(Of LocalDeclarationStatementSyntax, ModifiedIdentifierSyntax, VariableDeclaratorSyntax) @@ -22,4 +23,4 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.RemoveUnusedVariable End Get End Property End Class -End Namespace +End Namespace \ No newline at end of file From aa9fd5fa2f9f4ce2e5a56aaf42bb9004b398fae3 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 21:51:45 -0700 Subject: [PATCH 160/214] Formatting. --- .../RemoveUnusedVariable/RemoveUnusedVariableTest.cs | 4 +--- .../RemoveUnusedVariable/RemoveUnusedVariableTest.vb | 3 +-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs b/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs index 25a7aba5cc308..6afc81cf3de69 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.cs @@ -13,9 +13,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics.RemoveUnuse public partial class RemoveUnusedVariableTest : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest { internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace) - { - return(null, new CSharpRemoveUnusedVariableCodeFixProvider()); - } + => (null, new CSharpRemoveUnusedVariableCodeFixProvider()); [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnusedVariable)] public async Task RemoveUnusedVariable() diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb index d2474f23d44ee..15fce1a1aa589 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/RemoveUnusedVariable/RemoveUnusedVariableTest.vb @@ -9,8 +9,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider) - Return (Nothing, - New VisualBasicRemoveUnusedVariableCodeFixProvider()) + Return (Nothing, New VisualBasicRemoveUnusedVariableCodeFixProvider()) End Function From 86e3f08b659b63c4056b1ae6dd8dc73a28f1e91d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Fri, 5 May 2017 14:04:13 -0700 Subject: [PATCH 161/214] Improve path completion providers (#19195) * Improve path completion providers * Make GAC completion faster and cancellable * Move FileSystemCompletionHelper.cs * Move LoadDirectiveCompletionProviderTests.cs * PR feedback and fix completion for 'X:' paths --- src/Compilers/Core/Portable/CorLightup.cs | 16 + .../ClrGlobalAssemblyCache.cs | 2 +- .../CSharpEditorServicesTest.csproj | 5 +- ...lobalAssemblyCacheCompletionHelperTests.cs | 76 ----- .../LoadDirectiveCompletionProviderTests.cs | 69 +++++ ...ferenceDirectiveCompletionProviderTests.cs | 89 ++---- .../LoadDirectiveCompletionProviderTests.cs | 86 ------ src/EditorFeatures/Core/EditorFeatures.csproj | 2 - .../FileSystem/FileSystemCompletionHelper.cs | 289 ------------------ .../FileSystem/PathCompletionUtilities.cs | 108 ------- .../FileSystemCompletionHelperTests.cs | 234 ++++++++++++++ ...lobalAssemblyCacheCompletionHelperTests.cs | 44 +++ .../TestFileSystemCompletionHelper.cs | 62 ++++ .../Test/EditorServicesTest.csproj | 3 + .../Completion/FileSystemCompletionHelper.cs | 252 +++++++++++++++ src/Features/Core/Portable/Features.csproj | 1 + .../CSharpInteractiveEditorFeatures.csproj | 1 + .../DirectiveCompletionProviderUtilities.cs | 32 ++ .../LoadDirectiveCompletionProvider.cs | 116 +------ .../ReferenceDirectiveCompletionProvider.cs | 26 +- ...AbstractDirectivePathCompletionProvider.cs | 143 +++++++++ ...AbstractLoadDirectiveCompletionProvider.cs | 61 ++++ ...actReferenceDirectiveCompletionProvider.cs | 121 +++----- .../GlobalAssemblyCacheCompletionHelper.cs | 60 ++-- .../Core/InteractiveEditorFeatures.csproj | 2 + .../BasicInteractiveEditorFeatures.vbproj | 2 + .../DirectiveCompletionProviderUtilities.vb | 23 ++ .../LoadDirectiveCompletionProvider.vb | 19 ++ .../ReferenceDirectiveCompletionProvider.vb | 13 +- .../Portable/Utilities/StringEscapeEncoder.cs | 6 +- .../UtilityTest/StringEscapingTests.cs | 5 +- 31 files changed, 1080 insertions(+), 888 deletions(-) delete mode 100644 src/EditorFeatures/CSharpTest/Completion/CompletionProviders/GlobalAssemblyCacheCompletionHelperTests.cs create mode 100644 src/EditorFeatures/CSharpTest/Completion/CompletionProviders/LoadDirectiveCompletionProviderTests.cs rename src/EditorFeatures/CSharpTest/Completion/{ => CompletionProviders}/ReferenceDirectiveCompletionProviderTests.cs (55%) delete mode 100644 src/EditorFeatures/CSharpTest/Completion/LoadDirectiveCompletionProviderTests.cs delete mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/FileSystemCompletionHelper.cs delete mode 100644 src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/PathCompletionUtilities.cs create mode 100644 src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs create mode 100644 src/EditorFeatures/Test/Completion/GlobalAssemblyCacheCompletionHelperTests.cs create mode 100644 src/EditorFeatures/Test/Completion/TestFileSystemCompletionHelper.cs create mode 100644 src/Features/Core/Portable/Completion/FileSystemCompletionHelper.cs create mode 100644 src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/DirectiveCompletionProviderUtilities.cs create mode 100644 src/Interactive/EditorFeatures/Core/Completion/AbstractDirectivePathCompletionProvider.cs create mode 100644 src/Interactive/EditorFeatures/Core/Completion/AbstractLoadDirectiveCompletionProvider.cs create mode 100644 src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/DirectiveCompletionProviderUtilities.vb create mode 100644 src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/LoadDirectiveCompletionProvider.vb diff --git a/src/Compilers/Core/Portable/CorLightup.cs b/src/Compilers/Core/Portable/CorLightup.cs index 9138ea0e43d68..c1c1428711ae4 100644 --- a/src/Compilers/Core/Portable/CorLightup.cs +++ b/src/Compilers/Core/Portable/CorLightup.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; using System.Globalization; +using System.IO; using System.Reflection; namespace Roslyn.Utilities @@ -49,6 +50,21 @@ private static class CultureTypes } } + private static class _Directory + { + internal static readonly Type Type = typeof(Directory); + + internal static readonly Func s_getLogicalDrivesOpt = Type + .GetTypeInfo() + .GetDeclaredMethod("GetLogicalDrives")? + .CreateDelegate>(); + } + + internal static string[] GetLogicalDrives() + { + return _Directory.s_getLogicalDrivesOpt?.Invoke() ?? Array.Empty(); + } + private static class _Assembly { internal static readonly Type Type = typeof(Assembly); diff --git a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs index 39c4e0bb38c80..c5bab29f78de3 100644 --- a/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs +++ b/src/Compilers/Shared/GlobalAssemblyCacheHelpers/ClrGlobalAssemblyCache.cs @@ -143,7 +143,7 @@ private static IEnumerable GetAssemblyIdentities( else if (hr != S_OK) { Exception e = Marshal.GetExceptionForHR(hr); - if (e is FileNotFoundException) + if (e is FileNotFoundException || e is DirectoryNotFoundException) { // invalid assembly name: yield break; diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index e127ecce924f6..52d3cdfc1212c 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -294,7 +294,6 @@ - @@ -307,8 +306,8 @@ - - + + diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/GlobalAssemblyCacheCompletionHelperTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/GlobalAssemblyCacheCompletionHelperTests.cs deleted file mode 100644 index 890aa50a1db85..0000000000000 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/GlobalAssemblyCacheCompletionHelperTests.cs +++ /dev/null @@ -1,76 +0,0 @@ -// 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 Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.Completion.FileSystem; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Test.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.IntelliSense.CompletionSetSources -{ - public class GlobalAssemblyCacheCompletionHelperTests - { - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public void ExistingReference() - { - var code = "System.Windows"; - VerifyPresence(code, "System.Windows.Forms"); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public void FullReferenceIdentity() - { - var code = "System,"; - VerifyPresence(code, typeof(System.Diagnostics.Process).Assembly.FullName); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public void FullReferenceIdentityDescription() - { - var code = "System"; - var completions = GetItems(code); - var systemsColl = from completion in completions - where completion.DisplayText == "System" - select completion; - - Assert.True(systemsColl.Any( - completion => CommonCompletionItem.GetDescription(completion).Text == typeof(System.Diagnostics.Process).Assembly.FullName)); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public void NothingOnForwardSlash() - { - var code = "System.Windows/"; - VerifyAbsence(code); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public void NothingOnBackSlash() - { - var code = @"System.Windows\"; - VerifyAbsence(code); - } - - private static void VerifyPresence(string pathSoFar, string completionItem) - { - var completions = GetItems(pathSoFar); - Assert.True(completions.Any(c => c.DisplayText == completionItem)); - } - - private static void VerifyAbsence(string pathSoFar) - { - var completions = GetItems(pathSoFar); - Assert.True(completions == null || !completions.Any(), "Expected null or non-empty completions"); - } - - private static IEnumerable GetItems(string pathSoFar) - { - var helper = new GlobalAssemblyCacheCompletionHelper(null, new TextSpan()); - - return helper.GetItems(pathSoFar, documentPath: null); - } - } -} diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/LoadDirectiveCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/LoadDirectiveCompletionProviderTests.cs new file mode 100644 index 0000000000000..f0ef0ce6d1106 --- /dev/null +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/LoadDirectiveCompletionProviderTests.cs @@ -0,0 +1,69 @@ +// 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.IO; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem; +using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders +{ + [Trait(Traits.Feature, Traits.Features.Completion)] + public class LoadDirectiveCompletionProviderTests : AbstractCSharpCompletionProviderTests + { + public LoadDirectiveCompletionProviderTests(CSharpTestWorkspaceFixture workspaceFixture) : base(workspaceFixture) + { + } + + internal override CompletionProvider CreateCompletionProvider() + { + return new LoadDirectiveCompletionProvider(); + } + + protected override bool CompareItems(string actualItem, string expectedItem) + { + return actualItem.Equals(expectedItem, StringComparison.OrdinalIgnoreCase); + } + + protected override Task VerifyWorkerAsync( + string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, + SourceCodeKind sourceCodeKind, bool usePreviousCharAsTrigger, bool checkForAbsence, + int? glyph, int? matchPriority, bool? hasSuggestionItem) + { + return BaseVerifyWorkerAsync( + code, position, expectedItemOrNull, expectedDescriptionOrNull, + sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, + glyph, matchPriority, hasSuggestionItem); + } + + [Fact] + public async Task IsCommitCharacterTest() + { + var commitCharacters = new[] { '"', '\\' }; + await VerifyCommitCharactersAsync("#load \"$$", textTypedSoFar: "", validChars: commitCharacters); + } + + [Fact] + public void IsTextualTriggerCharacterTest() + { + var validMarkupList = new[] + { + "#load \"$$/", + "#load \"$$\\", + "#load \"$$,", + "#load \"$$A", + "#load \"$$!", + "#load \"$$(", + }; + + foreach (var markup in validMarkupList) + { + VerifyTextualTriggerCharacter(markup, shouldTriggerWithTriggerOnLettersEnabled: true, shouldTriggerWithTriggerOnLettersDisabled: true); + } + } + } +} diff --git a/src/EditorFeatures/CSharpTest/Completion/ReferenceDirectiveCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ReferenceDirectiveCompletionProviderTests.cs similarity index 55% rename from src/EditorFeatures/CSharpTest/Completion/ReferenceDirectiveCompletionProviderTests.cs rename to src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ReferenceDirectiveCompletionProviderTests.cs index 1004329dbd9d5..67d4b98cac117 100644 --- a/src/EditorFeatures/CSharpTest/Completion/ReferenceDirectiveCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/ReferenceDirectiveCompletionProviderTests.cs @@ -6,13 +6,14 @@ using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; using Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; using Roslyn.Test.Utilities; +using Roslyn.Utilities; using Xunit; -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.IntelliSense.CompletionSetSources +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders { + [Trait(Traits.Feature, Traits.Features.Completion)] public class ReferenceDirectiveCompletionProviderTests : AbstractCSharpCompletionProviderTests { public ReferenceDirectiveCompletionProviderTests(CSharpTestWorkspaceFixture workspaceFixture) : base(workspaceFixture) @@ -40,50 +41,33 @@ protected override Task VerifyWorkerAsync( glyph, matchPriority, hasSuggestionItem); } - private async Task VerifyItemsExistInScriptAndInteractiveAsync(string code, params string[] expected) - { - foreach (var ex in expected) - { - await VerifyItemExistsAsync(code, ex, expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); - } - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [Fact] public async Task IsCommitCharacterTest() { - var commitCharacters = new[] { '"', '\\', ',' }; + var commitCharacters = PathUtilities.IsUnixLikePlatform ? new[] { '"', '/' } : new[] { '"', '\\', '/', ',' }; await VerifyCommitCharactersAsync("#r \"$$", textTypedSoFar: "", validChars: commitCharacters); } - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [Fact] public void IsTextualTriggerCharacterTest() { var validMarkupList = new[] { + "#r \"$$/", "#r \"$$\\", "#r \"$$,", - "#r \"$$A" - }; - - foreach (var markup in validMarkupList) - { - VerifyTextualTriggerCharacter(markup, shouldTriggerWithTriggerOnLettersEnabled: true, shouldTriggerWithTriggerOnLettersDisabled: true); - } - - var invalidMarkupList = new[] - { - "#r \"$$/", + "#r \"$$A", "#r \"$$!", "#r \"$$(", }; - foreach (var markup in invalidMarkupList) + foreach (var markup in validMarkupList) { - VerifyTextualTriggerCharacter(markup, shouldTriggerWithTriggerOnLettersEnabled: false, shouldTriggerWithTriggerOnLettersDisabled: false); + VerifyTextualTriggerCharacter(markup, shouldTriggerWithTriggerOnLettersEnabled: true, shouldTriggerWithTriggerOnLettersDisabled: true); } } - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [ConditionalFact(typeof(WindowsOnly))] public async Task SendEnterThroughToEditorTest() { await VerifySendEnterThroughToEnterAsync("#r \"System$$", "System", sendThroughEnterOption: EnterKeyRule.Never, expected: false); @@ -91,57 +75,34 @@ public async Task SendEnterThroughToEditorTest() await VerifySendEnterThroughToEnterAsync("#r \"System$$", "System", sendThroughEnterOption: EnterKeyRule.Always, expected: false); // note: GAC completion helper uses its own EnterKeyRule } - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task RootDrives() - { - // ensure drives are listed without the trailing backslash - var drive = Environment.GetLogicalDrives().First().TrimEnd('\\'); - await VerifyItemsExistInScriptAndInteractiveAsync( - "#r \"$$", - drive); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task RelativeDirectories() - { - await VerifyItemsExistInScriptAndInteractiveAsync( - "#r \"$$", - ".", - ".."); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task GACReference() + [ConditionalFact(typeof(WindowsOnly))] + public async Task GacReference() { - await VerifyItemsExistInScriptAndInteractiveAsync( - "#r \"$$", - "System.Windows.Forms"); + await VerifyItemExistsAsync("#r \"$$", "System.Windows.Forms", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); } - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task GACReferenceFullyQualified() + [ConditionalFact(typeof(WindowsOnly))] + public async Task GacReferenceFullyQualified() { - await VerifyItemsExistInScriptAndInteractiveAsync( - "#r \"System.Windows.Forms,$$", - "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); + await VerifyItemExistsAsync( + "#r \"System.Windows.Forms,$$", + "System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089", expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); } - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] + [ConditionalFact(typeof(WindowsOnly))] public async Task FileSystemReference() { - string systemDir = Path.GetFullPath(Environment.SystemDirectory); - DirectoryInfo windowsDir = System.IO.Directory.GetParent(systemDir); - string windowsDirPath = windowsDir.FullName; - string windowsRoot = System.IO.Directory.GetDirectoryRoot(systemDir); + var systemDir = Path.GetFullPath(Environment.SystemDirectory); + var windowsDir = Directory.GetParent(systemDir); + var windowsDirPath = windowsDir.FullName; + var windowsRoot = Directory.GetDirectoryRoot(systemDir); // we need to get the exact casing from the file system: var normalizedWindowsPath = Directory.GetDirectories(windowsRoot, windowsDir.Name).Single(); var windowsFolderName = Path.GetFileName(normalizedWindowsPath); var code = "#r \"" + windowsRoot + "$$"; - await VerifyItemsExistInScriptAndInteractiveAsync( - code, - windowsFolderName); + await VerifyItemExistsAsync(code, windowsFolderName, expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script); } } } \ No newline at end of file diff --git a/src/EditorFeatures/CSharpTest/Completion/LoadDirectiveCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/LoadDirectiveCompletionProviderTests.cs deleted file mode 100644 index 44a4914fc15af..0000000000000 --- a/src/EditorFeatures/CSharpTest/Completion/LoadDirectiveCompletionProviderTests.cs +++ /dev/null @@ -1,86 +0,0 @@ -// 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.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem; -using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion.CompletionProviders; -using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; -using Roslyn.Test.Utilities; -using Xunit; - -namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Completion -{ - public class LoadDirectiveCompletionProviderTests : AbstractCSharpCompletionProviderTests - { - public LoadDirectiveCompletionProviderTests(CSharpTestWorkspaceFixture workspaceFixture) : base(workspaceFixture) - { - } - - internal override CompletionProvider CreateCompletionProvider() - { - return new LoadDirectiveCompletionProvider(); - } - - protected override bool CompareItems(string actualItem, string expectedItem) - { - return actualItem.Equals(expectedItem, StringComparison.OrdinalIgnoreCase); - } - - protected override Task VerifyWorkerAsync( - string code, int position, string expectedItemOrNull, string expectedDescriptionOrNull, - SourceCodeKind sourceCodeKind, bool usePreviousCharAsTrigger, bool checkForAbsence, - int? glyph, int? matchPriority, bool? hasSuggestionItem) - { - return BaseVerifyWorkerAsync( - code, position, expectedItemOrNull, expectedDescriptionOrNull, - sourceCodeKind, usePreviousCharAsTrigger, checkForAbsence, - glyph, matchPriority, hasSuggestionItem); - } - - private async Task VerifyItemExistsInScriptAsync(string markup, string expected) - { - await VerifyItemExistsAsync(markup, expected, expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script, usePreviousCharAsTrigger: false); - } - - private async Task VerifyItemIsAbsentInInteractiveAsync(string markup, string expected) - { - await VerifyItemIsAbsentAsync(markup, expected, expectedDescriptionOrNull: null, sourceCodeKind: SourceCodeKind.Script, usePreviousCharAsTrigger: false); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task NetworkPath() - { - await VerifyItemExistsInScriptAsync( - @"#load ""$$", - @"\\"); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task NetworkPathAfterInitialBackslash() - { - await VerifyItemExistsInScriptAsync( - @"#load ""\$$", - @"\\"); - } - - [Fact, Trait(Traits.Feature, Traits.Features.Completion)] - public async Task UpOneDirectoryNotShownAtRoot() - { - // after so many ".." we should be at the root drive an should no longer suggest the parent. we can determine - // our current directory depth by counting the number of backslashes present in the current working directory - // and after that many references to "..", we are at the root. - int depth = Directory.GetCurrentDirectory().Count(c => c == Path.DirectorySeparatorChar); - var pathToRoot = string.Concat(Enumerable.Repeat(@"..\", depth)); - - await VerifyItemExistsInScriptAsync( - @"#load ""$$", - ".."); - await VerifyItemIsAbsentInInteractiveAsync( - @"#load """ + pathToRoot + "$$", - ".."); - } - } -} diff --git a/src/EditorFeatures/Core/EditorFeatures.csproj b/src/EditorFeatures/Core/EditorFeatures.csproj index 18b9836a0c890..3a5b371a27e54 100644 --- a/src/EditorFeatures/Core/EditorFeatures.csproj +++ b/src/EditorFeatures/Core/EditorFeatures.csproj @@ -466,9 +466,7 @@ - - diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/FileSystemCompletionHelper.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/FileSystemCompletionHelper.cs deleted file mode 100644 index e334b9a4a8896..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/FileSystemCompletionHelper.cs +++ /dev/null @@ -1,289 +0,0 @@ -// 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.Diagnostics; -using System.IO; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Shared.Extensions; -using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem -{ - internal sealed class FileSystemCompletionHelper - { - private readonly ICurrentWorkingDirectoryDiscoveryService _fileSystemDiscoveryService; - private readonly Func _exclude; - private readonly Glyph _folderGlyph; - private readonly Glyph _fileGlyph; - - // absolute paths - private readonly ImmutableArray _searchPaths; - - private readonly ISet _allowableExtensions; - - private readonly Lazy _lazyGetDrives; - private readonly CompletionProvider _completionProvider; - private readonly TextSpan _textChangeSpan; - private readonly CompletionItemRules _itemRules; - - public FileSystemCompletionHelper( - CompletionProvider completionProvider, - TextSpan textChangeSpan, - ICurrentWorkingDirectoryDiscoveryService fileSystemDiscoveryService, - Glyph folderGlyph, - Glyph fileGlyph, - ImmutableArray searchPaths, - IEnumerable allowableExtensions, - Func exclude = null, - CompletionItemRules itemRules = null) - { - Debug.Assert(searchPaths.All(path => PathUtilities.IsAbsolute(path))); - - _completionProvider = completionProvider; - _textChangeSpan = textChangeSpan; - _searchPaths = searchPaths; - _allowableExtensions = allowableExtensions.Select(e => e.ToLowerInvariant()).ToSet(); - _fileSystemDiscoveryService = fileSystemDiscoveryService; - _folderGlyph = folderGlyph; - _fileGlyph = fileGlyph; - _exclude = exclude; - _itemRules = itemRules; - - _lazyGetDrives = new Lazy(() => - IOUtilities.PerformIO(Directory.GetLogicalDrives, Array.Empty())); - } - - public ImmutableArray GetItems(string pathSoFar, string documentPath) - { - if (_exclude != null && _exclude(pathSoFar)) - { - return ImmutableArray.Empty; - } - - return GetFilesAndDirectories(pathSoFar, documentPath); - } - - private CompletionItem CreateCurrentDirectoryItem() - { - return CommonCompletionItem.Create(".", rules: _itemRules); - } - - private CompletionItem CreateParentDirectoryItem() - { - return CommonCompletionItem.Create("..", rules: _itemRules); - } - - private CompletionItem CreateNetworkRoot(TextSpan textChangeSpan) - { - return CommonCompletionItem.Create("\\\\", rules: _itemRules); - } - - private ImmutableArray GetFilesAndDirectories(string path, string basePath) - { - var result = ArrayBuilder.GetInstance(); - var pathKind = PathUtilities.GetPathKind(path); - switch (pathKind) - { - case PathKind.Empty: - result.Add(CreateCurrentDirectoryItem()); - - if (!IsDriveRoot(_fileSystemDiscoveryService.WorkingDirectory)) - { - result.Add(CreateParentDirectoryItem()); - } - - result.Add(CreateNetworkRoot(_textChangeSpan)); - result.AddRange(GetLogicalDrives()); - result.AddRange(GetFilesAndDirectoriesInSearchPaths()); - break; - - case PathKind.Absolute: - case PathKind.RelativeToCurrentDirectory: - case PathKind.RelativeToCurrentParent: - case PathKind.RelativeToCurrentRoot: - { - var fullPath = FileUtilities.ResolveRelativePath( - path, - basePath, - _fileSystemDiscoveryService.WorkingDirectory); - - if (fullPath != null) - { - result.AddRange(GetFilesAndDirectoriesInDirectory(fullPath)); - - // although it is possible to type "." here, it doesn't make any sense to do so: - if (!IsDriveRoot(fullPath) && pathKind != PathKind.Absolute) - { - result.Add(CreateParentDirectoryItem()); - } - - if (path == "\\" && pathKind == PathKind.RelativeToCurrentRoot) - { - // The user has typed only "\". In this case, we want to add "\\" to - // the list. Also, the textChangeSpan needs to be backed up by one - // so that it will consume the "\" when "\\" is inserted. - result.Add(CreateNetworkRoot(TextSpan.FromBounds(_textChangeSpan.Start - 1, _textChangeSpan.End))); - } - } - else - { - // invalid path - result.Clear(); - } - } - - break; - - case PathKind.Relative: - - // although it is possible to type "." here, it doesn't make any sense to do so: - result.Add(CreateParentDirectoryItem()); - - foreach (var searchPath in _searchPaths) - { - var fullPath = PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, path); - - // search paths are always absolute: - Debug.Assert(PathUtilities.IsAbsolute(fullPath)); - result.AddRange(GetFilesAndDirectoriesInDirectory(fullPath)); - } - - break; - - case PathKind.RelativeToDriveDirectory: - // these paths are not supported - break; - - default: - throw ExceptionUtilities.UnexpectedValue(pathKind); - } - - return result.ToImmutableAndFree(); - } - - private static bool IsDriveRoot(string fullPath) - { - return IOUtilities.PerformIO(() => new DirectoryInfo(fullPath).Parent == null); - } - - private IEnumerable GetFilesAndDirectoriesInDirectory(string fullDirectoryPath) - { - Debug.Assert(PathUtilities.IsAbsolute(fullDirectoryPath)); - if (IOUtilities.PerformIO(() => Directory.Exists(fullDirectoryPath))) - { - var directoryInfo = IOUtilities.PerformIO(() => new DirectoryInfo(fullDirectoryPath)); - if (directoryInfo != null) - { - return from child in GetFileSystemInfos(directoryInfo) - where ShouldShow(child) - where CanAccess(child) - select this.CreateCompletion(child); - } - } - - return SpecializedCollections.EmptyEnumerable(); - } - - private CompletionItem CreateCompletion(FileSystemInfo child) - { - return CommonCompletionItem.Create( - child.Name, - glyph: child is DirectoryInfo ? _folderGlyph : _fileGlyph, - description: child.FullName.ToSymbolDisplayParts(), - rules: _itemRules); - } - - private bool ShouldShow(FileSystemInfo child) - { - // Get the attributes. If we can't, assume it's hidden. - var attributes = IOUtilities.PerformIO(() => child.Attributes, FileAttributes.Hidden); - - // Don't show hidden/system files. - if ((attributes & FileAttributes.Hidden) != 0 || - (attributes & FileAttributes.System) != 0) - { - return false; - } - - if (child is DirectoryInfo) - { - return true; - } - - if (child is FileInfo) - { - return - _allowableExtensions.Count == 0 || - _allowableExtensions.Contains(Path.GetExtension(child.Name).ToLowerInvariant()); - } - - return false; - } - - private bool CanAccess(FileSystemInfo info) - { - switch (info) - { - case DirectoryInfo d: return CanAccessDirectory(d); - case FileInfo f: return CanAccessFile(f); - } - - return false; - } - - private bool CanAccessFile(FileInfo file) - { - var accessControl = IOUtilities.PerformIO(file.GetAccessControl); - - // Quick and dirty check. If we can't even get the access control object, then we - // can't access the file. - if (accessControl == null) - { - return false; - } - - // TODO(cyrusn): Actually add checks here. - return true; - } - - private bool CanAccessDirectory(DirectoryInfo directory) - { - var accessControl = IOUtilities.PerformIO(directory.GetAccessControl); - - // Quick and dirty check. If we can't even get the access control object, then we - // can't access the file. - if (accessControl == null) - { - return false; - } - - // TODO(cyrusn): Do more checks here. - return true; - } - - private IEnumerable GetFilesAndDirectoriesInSearchPaths() - { - return _searchPaths.SelectMany(GetFilesAndDirectoriesInDirectory); - } - - private IEnumerable GetLogicalDrives() - { - // First, we may have a filename, so let's include all drives - return from d in _lazyGetDrives.Value - where d.Length > 0 && (d.Last() == Path.DirectorySeparatorChar || d.Last() == Path.AltDirectorySeparatorChar) - let text = d.Substring(0, d.Length - 1) - select CommonCompletionItem.Create(text, glyph: _folderGlyph, rules: _itemRules); - } - - private static FileSystemInfo[] GetFileSystemInfos(DirectoryInfo directoryInfo) - { - return IOUtilities.PerformIO(directoryInfo.GetFileSystemInfos, Array.Empty()); - } - } -} diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/PathCompletionUtilities.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/PathCompletionUtilities.cs deleted file mode 100644 index 2f6774440b78c..0000000000000 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/FileSystem/PathCompletionUtilities.cs +++ /dev/null @@ -1,108 +0,0 @@ -// 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.Completion; -using Microsoft.CodeAnalysis.Text; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem -{ - internal static class PathCompletionUtilities - { - internal static bool IsTriggerCharacter(SourceText text, int characterPosition) - { - // Bring up completion when the user types a quote (i.e.: #r "), or if they type a slash - // path separator character, or if they type a comma (#r "foo,version..."). - // - // Also, if they're starting a word. i.e. #r "c:\W - var ch = text[characterPosition]; - return ch == '"' || ch == '\\' || ch == ',' || - CommonCompletionUtilities.IsStartingNewWord(text, characterPosition, char.IsLetter, char.IsLetterOrDigit); - } - - internal static bool IsFilterCharacter(CompletionItem item, char ch, string textTypedSoFar) - { - // If the user types something that matches what is in the completion list, then just - // count it as a filter character. - - // For example, if they type "Program " and "Program Files" is in the list, the - // should be counted as a filter character and not a commit character. - return item.DisplayText.StartsWith(textTypedSoFar, StringComparison.OrdinalIgnoreCase); - } - - internal static bool IsCommitcharacter(CompletionItem item, char ch, string textTypedSoFar) - { - return ch == '"' || ch == '\\' || ch == ','; - } - - internal static bool SendEnterThroughToEditor(CompletionItem item, string textTypedSoFar) - { - return false; - } - - internal static string GetPathThroughLastSlash(string quotedPath, int quotedPathStart, int position) - { - Contract.ThrowIfTrue(quotedPath[0] != '"'); - - const int QuoteLength = 1; - - var positionInQuotedPath = position - quotedPathStart; - var path = quotedPath.Substring(QuoteLength, positionInQuotedPath - QuoteLength).Trim(); - var afterLastSlashIndex = AfterLastSlashIndex(path, path.Length); - - // We want the portion up to, and including the last slash if there is one. That way if - // the user pops up completion in the middle of a path (i.e. "C:\Win") then we'll - // consider the path to be "C:\" and we will show appropriate completions. - return afterLastSlashIndex >= 0 ? path.Substring(0, afterLastSlashIndex) : path; - } - - internal static TextSpan GetTextChangeSpan(string quotedPath, int quotedPathStart, int position) - { - // We want the text change to be from after the last slash to the end of the quoted - // path. If there is no last slash, then we want it from right after the start quote - // character. - var positionInQuotedPath = position - quotedPathStart; - - // Where we want to start tracking is right after the slash (if we have one), or else - // right after the string starts. - var afterLastSlashIndex = PathCompletionUtilities.AfterLastSlashIndex(quotedPath, positionInQuotedPath); - var afterFirstQuote = 1; - - var startIndex = Math.Max(afterLastSlashIndex, afterFirstQuote); - var endIndex = quotedPath.Length; - - // If the string ends with a quote, the we do not want to consume that. - if (EndsWithQuote(quotedPath)) - { - endIndex--; - } - - return TextSpan.FromBounds(startIndex + quotedPathStart, endIndex + quotedPathStart); - } - - internal static bool EndsWithQuote(string quotedPath) - { - return quotedPath.Length >= 2 && quotedPath[quotedPath.Length - 1] == '"'; - } - - /// - /// Returns the index right after the last slash that precedes 'position'. If there is no - /// slash in the string, -1 is returned. - /// - private static int AfterLastSlashIndex(string text, int position) - { - // Position might be out of bounds of the string (if the string is unterminated. Make - // sure it's within bounds. - position = Math.Min(position, text.Length - 1); - - int index; - if ((index = text.LastIndexOf('/', position)) >= 0 || - (index = text.LastIndexOf('\\', position)) >= 0) - { - return index + 1; - } - - return -1; - } - } -} diff --git a/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs new file mode 100644 index 0000000000000..75c0a5a0af3a0 --- /dev/null +++ b/src/EditorFeatures/Test/Completion/FileSystemCompletionHelperTests.cs @@ -0,0 +1,234 @@ +// 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.Immutable; +using System.Linq; +using System.Threading; +using Microsoft.CodeAnalysis.Completion; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Completion +{ + public class FileSystemCompletionHelperTests + { + private void AssertItemsEqual(ImmutableArray actual, params string[] expected) + { + AssertEx.Equal( + expected, + actual.Select(c => $"'{c.DisplayText}', {string.Join(", ", c.Tags)}, '{c.Properties["Description"]}'"), + itemInspector: c => $"@\"{c}\""); + + Assert.True(actual.All(i => i.Rules == TestFileSystemCompletionHelper.CompletionRules)); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void GetItems_Windows1() + { + var fsc = new TestFileSystemCompletionHelper( + searchPaths: ImmutableArray.Create(@"X:\A", @"X:\B"), + baseDirectoryOpt: @"Z:\C", + allowableExtensions: ImmutableArray.Create(".abc", ".def"), + drives: new[] { @"X:\", @"Z:\" }, + directories: new[] + { + @"X:", + @"X:\A", + @"X:\A\1", + @"X:\A\2", + @"X:\A\3", + @"X:\B", + @"Z:", + @"Z:\C", + @"Z:\D", + }, + files: new[] + { + @"X:\A\1\file1.abc", + @"X:\A\2\file2.abc", + @"X:\B\file4.x", + @"X:\B\file5.abc", + @"X:\B\hidden.def", + @"Z:\C\file6.def", + @"Z:\C\file.7.def", + }); + + // Note backslashes in description are escaped + AssertItemsEqual(fsc.GetItems("", CancellationToken.None), + @"'file6.def', File, C#, 'Text|Z:\5CC\5Cfile6.def'", + @"'file.7.def', File, C#, 'Text|Z:\5CC\5Cfile.7.def'", + @"'X:', Folder, 'Text|X:'", + @"'Z:', Folder, 'Text|Z:'", + @"'\\', , 'Text|\5C\5C'", + @"'1', Folder, 'Text|X:\5CA\5C1'", + @"'2', Folder, 'Text|X:\5CA\5C2'", + @"'3', Folder, 'Text|X:\5CA\5C3'", + @"'file5.abc', File, C#, 'Text|X:\5CB\5Cfile5.abc'"); + + AssertItemsEqual(fsc.GetItems(@"X:\A\", CancellationToken.None), + @"'1', Folder, 'Text|X:\5CA\5C1'", + @"'2', Folder, 'Text|X:\5CA\5C2'", + @"'3', Folder, 'Text|X:\5CA\5C3'"); + + AssertItemsEqual(fsc.GetItems(@"X:\B\", CancellationToken.None), + @"'file5.abc', File, C#, 'Text|X:\5CB\5Cfile5.abc'"); + + AssertItemsEqual(fsc.GetItems(@"Z:\", CancellationToken.None), + @"'C', Folder, 'Text|Z:\5CC'", + @"'D', Folder, 'Text|Z:\5CD'"); + + AssertItemsEqual(fsc.GetItems(@"Z:", CancellationToken.None), + @"'Z:', Folder, 'Text|Z:'"); + + AssertItemsEqual(fsc.GetItems(@"\", CancellationToken.None), + @"'\\', , 'Text|\5C\5C'"); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void GetItems_Windows_NoBaseDirectory() + { + var fsc = new TestFileSystemCompletionHelper( + searchPaths: ImmutableArray.Create(@"X:\A", @"X:\B"), + baseDirectoryOpt: null, + allowableExtensions: ImmutableArray.Create(".abc", ".def"), + drives: new[] { @"X:\" }, + directories: new[] + { + @"X:", + @"X:\A", + @"X:\A\1", + @"X:\A\2", + @"X:\A\3", + @"X:\B", + }, + files: new[] + { + @"X:\A\1\file1.abc", + @"X:\A\2\file2.abc", + @"X:\B\file4.x", + @"X:\B\file5.abc", + @"X:\B\hidden.def", + }); + + // Note backslashes in description are escaped + AssertItemsEqual(fsc.GetItems(@"", CancellationToken.None), + @"'X:', Folder, 'Text|X:'", + @"'\\', , 'Text|\5C\5C'", + @"'1', Folder, 'Text|X:\5CA\5C1'", + @"'2', Folder, 'Text|X:\5CA\5C2'", + @"'3', Folder, 'Text|X:\5CA\5C3'", + @"'file5.abc', File, C#, 'Text|X:\5CB\5Cfile5.abc'"); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void GetItems_Windows_NoSearchPaths() + { + var fsc = new TestFileSystemCompletionHelper( + searchPaths: ImmutableArray.Empty, + baseDirectoryOpt: null, + allowableExtensions: ImmutableArray.Create(".abc", ".def"), + drives: new[] { @"X:\" }, + directories: new[] + { + @"X:", + @"X:\A", + @"X:\A\1", + @"X:\A\2", + @"X:\A\3", + @"X:\B", + }, + files: new[] + { + @"X:\A\1\file1.abc", + @"X:\A\2\file2.abc", + @"X:\B\file4.x", + @"X:\B\file5.abc", + @"X:\B\hidden.def", + }); + + // Note backslashes in description are escaped + AssertItemsEqual(fsc.GetItems(@"", CancellationToken.None), + @"'X:', Folder, 'Text|X:'", + @"'\\', , 'Text|\5C\5C'"); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void GetItems_Windows_Network() + { + var fsc = new TestFileSystemCompletionHelper( + searchPaths: ImmutableArray.Empty, + baseDirectoryOpt: null, + allowableExtensions: ImmutableArray.Create(".cs"), + drives: Array.Empty(), + directories: new[] + { + @"\\server\share", + @"\\server\share\C", + @"\\server\share\D", + }, + files: new[] + { + @"\\server\share\C\b.cs", + @"\\server\share\C\c.cs", + @"\\server\share\D\e.cs", + }); + + AssertItemsEqual(fsc.GetItems(@"\\server\share\", CancellationToken.None), + @"'C', Folder, 'Text|\5C\5Cserver\5Cshare\5CC'", + @"'D', Folder, 'Text|\5C\5Cserver\5Cshare\5CD'"); + + AssertItemsEqual(fsc.GetItems(@"\\server\share\C\", CancellationToken.None), + @"'b.cs', File, C#, 'Text|\5C\5Cserver\5Cshare\5CC\5Cb.cs'", + @"'c.cs', File, C#, 'Text|\5C\5Cserver\5Cshare\5CC\5Cc.cs'"); + } + + [ConditionalFact(typeof(UnixLikeOnly))] + public void GetItems_Unix1() + { + var fsc = new TestFileSystemCompletionHelper( + searchPaths: ImmutableArray.Create(@"/A", @"/B"), + baseDirectoryOpt: @"/C", + allowableExtensions: ImmutableArray.Create(".abc", ".def"), + drives: Array.Empty(), + directories: new[] + { + @"/A", + @"/A/1", + @"/A/2", + @"/A/3", + @"/B", + @"/C", + @"/D", + }, + files: new[] + { + @"/A/1/file1.abc", + @"/A/2/file2.abc", + @"/B/file4.x", + @"/B/file5.abc", + @"/B/hidden.def", + @"/C/file6.def", + @"/C/file.7.def", + }); + + // Note backslashes in description are escaped + AssertItemsEqual(fsc.GetItems(@"", CancellationToken.None), + @"'file6.def', File, C#, 'Text|/C/file6.def'", + @"'file.7.def', File, C#, 'Text|/C/file.7.def'", + @"'/', Folder, 'Text|/'", + @"'1', Folder, 'Text|/A/1'", + @"'2', Folder, 'Text|/A/2'", + @"'3', Folder, 'Text|/A/3'", + @"'file5.abc', File, C#, 'Text|/B/file5.abc'"); + + AssertItemsEqual(fsc.GetItems(@"/", CancellationToken.None), + @"'A', Folder, 'Text|/A'", + @"'B', Folder, 'Text|/B'", + @"'C', Folder, 'Text|/C'", + @"'D', Folder, 'Text|/D'"); + + AssertItemsEqual(fsc.GetItems(@"/B/", CancellationToken.None), + @"'file5.abc', File, C#, 'Text|/B/file5.abc'"); + } + } +} diff --git a/src/EditorFeatures/Test/Completion/GlobalAssemblyCacheCompletionHelperTests.cs b/src/EditorFeatures/Test/Completion/GlobalAssemblyCacheCompletionHelperTests.cs new file mode 100644 index 0000000000000..ce5abe28ef17a --- /dev/null +++ b/src/EditorFeatures/Test/Completion/GlobalAssemblyCacheCompletionHelperTests.cs @@ -0,0 +1,44 @@ +// 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 Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Editor.Completion.FileSystem; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.IntelliSense.CompletionSetSources +{ + [Trait(Traits.Feature, Traits.Features.Completion)] + public class GlobalAssemblyCacheCompletionHelperTests + { + [ConditionalFact(typeof(WindowsOnly))] + public void ExistingReference() + { + var code = "System.Windows"; + VerifyPresence(code, "System.Windows.Forms"); + } + + [ConditionalFact(typeof(WindowsOnly))] + public void FullReferenceIdentity() + { + var code = "System,"; + VerifyPresence(code, typeof(System.Diagnostics.Process).Assembly.FullName); + } + + private static void VerifyPresence(string pathSoFar, string completionItem) + { + var completions = GetItems(pathSoFar); + Assert.True(completions.Any(c => c.DisplayText == completionItem)); + } + + private static IEnumerable GetItems(string pathSoFar) + { + var helper = new GlobalAssemblyCacheCompletionHelper(CompletionItemRules.Default); + return helper.GetItems(pathSoFar, CancellationToken.None); + } + } +} diff --git a/src/EditorFeatures/Test/Completion/TestFileSystemCompletionHelper.cs b/src/EditorFeatures/Test/Completion/TestFileSystemCompletionHelper.cs new file mode 100644 index 0000000000000..c1a8d9d180256 --- /dev/null +++ b/src/EditorFeatures/Test/Completion/TestFileSystemCompletionHelper.cs @@ -0,0 +1,62 @@ +// 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 Microsoft.CodeAnalysis.Completion; +using Roslyn.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.Editor.UnitTests.Completion +{ + internal sealed class TestFileSystemCompletionHelper : FileSystemCompletionHelper + { + internal static readonly CompletionItemRules CompletionRules = CompletionItemRules.Default; + + private readonly ImmutableArray _directories; + private readonly ImmutableArray _files; + private readonly ImmutableArray _drives; + + public TestFileSystemCompletionHelper( + ImmutableArray searchPaths, + string baseDirectoryOpt, + ImmutableArray allowableExtensions, + IEnumerable drives, + IEnumerable directories, + IEnumerable files) + : base(Glyph.OpenFolder, Glyph.CSharpFile, searchPaths, baseDirectoryOpt, allowableExtensions, CompletionRules) + { + Assert.True(drives.All(d => d.EndsWith(PathUtilities.DirectorySeparatorStr))); + Assert.True(directories.All(d => !d.EndsWith(PathUtilities.DirectorySeparatorStr))); + + _drives = ImmutableArray.CreateRange(drives); + _directories = ImmutableArray.CreateRange(directories); + _files = ImmutableArray.CreateRange(files); + } + + protected override string[] GetLogicalDrives() => + _drives.ToArray(); + + protected override bool IsVisibleFileSystemEntry(string fullPath) => + !fullPath.Contains("hidden"); + + protected override bool DirectoryExists(string fullPath) => + _directories.Contains(fullPath.TrimEnd(PathUtilities.DirectorySeparatorChar)); + + protected override IEnumerable EnumerateDirectories(string fullDirectoryPath) => + Enumerate(_directories, fullDirectoryPath); + + protected override IEnumerable EnumerateFiles(string fullDirectoryPath) => + Enumerate(_files, fullDirectoryPath); + + private IEnumerable Enumerate(ImmutableArray entries, string fullDirectoryPath) + { + var withTrailingSeparator = fullDirectoryPath.TrimEnd(PathUtilities.DirectorySeparatorChar) + PathUtilities.DirectorySeparatorChar; + return from d in entries + where d.StartsWith(withTrailingSeparator) + let nextSeparator = d.IndexOf(PathUtilities.DirectorySeparatorChar, withTrailingSeparator.Length) + select d.Substring(0, (nextSeparator >= 0) ? nextSeparator : d.Length); + } + } +} diff --git a/src/EditorFeatures/Test/EditorServicesTest.csproj b/src/EditorFeatures/Test/EditorServicesTest.csproj index 3e97cdf6e5b5e..2e67d5ab70e6b 100644 --- a/src/EditorFeatures/Test/EditorServicesTest.csproj +++ b/src/EditorFeatures/Test/EditorServicesTest.csproj @@ -224,6 +224,9 @@ + + + diff --git a/src/Features/Core/Portable/Completion/FileSystemCompletionHelper.cs b/src/Features/Core/Portable/Completion/FileSystemCompletionHelper.cs new file mode 100644 index 0000000000000..d432201d14c20 --- /dev/null +++ b/src/Features/Core/Portable/Completion/FileSystemCompletionHelper.cs @@ -0,0 +1,252 @@ +// 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.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Shared.Extensions; +using Microsoft.CodeAnalysis.Shared.Utilities; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Completion +{ + internal class FileSystemCompletionHelper + { + private static readonly char[] s_windowsDirectorySeparator = { '\\' }; + + private readonly Glyph _folderGlyph; + private readonly Glyph _fileGlyph; + + // absolute paths + private readonly ImmutableArray _searchPaths; + private readonly string _baseDirectoryOpt; + + private readonly ImmutableArray _allowableExtensions; + private readonly CompletionItemRules _itemRules; + + public FileSystemCompletionHelper( + Glyph folderGlyph, + Glyph fileGlyph, + ImmutableArray searchPaths, + string baseDirectoryOpt, + ImmutableArray allowableExtensions, + CompletionItemRules itemRules) + { + Debug.Assert(searchPaths.All(path => PathUtilities.IsAbsolute(path))); + Debug.Assert(baseDirectoryOpt == null || PathUtilities.IsAbsolute(baseDirectoryOpt)); + + _searchPaths = searchPaths; + _baseDirectoryOpt = baseDirectoryOpt; + _allowableExtensions = allowableExtensions; + _folderGlyph = folderGlyph; + _fileGlyph = fileGlyph; + _itemRules = itemRules; + } + + // virtual for testing + protected virtual string[] GetLogicalDrives() + => IOUtilities.PerformIO(CorLightup.Desktop.GetLogicalDrives, Array.Empty()); + + // virtual for testing + protected virtual bool DirectoryExists(string fullPath) + { + Debug.Assert(PathUtilities.IsAbsolute(fullPath)); + return Directory.Exists(fullPath); + } + + // virtual for testing + protected virtual IEnumerable EnumerateDirectories(string fullDirectoryPath) + { + Debug.Assert(PathUtilities.IsAbsolute(fullDirectoryPath)); + return IOUtilities.PerformIO(() => Directory.EnumerateDirectories(fullDirectoryPath), Array.Empty()); + } + + // virtual for testing + protected virtual IEnumerable EnumerateFiles(string fullDirectoryPath) + { + Debug.Assert(PathUtilities.IsAbsolute(fullDirectoryPath)); + return IOUtilities.PerformIO(() => Directory.EnumerateFiles(fullDirectoryPath), Array.Empty()); + } + + // virtual for testing + protected virtual bool IsVisibleFileSystemEntry(string fullPath) + { + Debug.Assert(PathUtilities.IsAbsolute(fullPath)); + return IOUtilities.PerformIO(() => (File.GetAttributes(fullPath) & (FileAttributes.Hidden | FileAttributes.System)) == 0, false); + } + + private CompletionItem CreateNetworkRoot() + => CommonCompletionItem.Create( + "\\\\", + glyph: null, + description: "\\\\".ToSymbolDisplayParts(), + rules: _itemRules); + + private CompletionItem CreateUnixRoot() + => CommonCompletionItem.Create( + "/", + glyph: _folderGlyph, + description: "/".ToSymbolDisplayParts(), + rules: _itemRules); + + private CompletionItem CreateFileSystemEntryItem(string fullPath, bool isDirectory) + => CommonCompletionItem.Create( + PathUtilities.GetFileName(fullPath), + glyph: isDirectory ? _folderGlyph : _fileGlyph, + description: fullPath.ToSymbolDisplayParts(), + rules: _itemRules); + + private CompletionItem CreateLogicalDriveItem(string drive) + => CommonCompletionItem.Create( + drive, + glyph: _folderGlyph, + description: drive.ToSymbolDisplayParts(), + rules: _itemRules); + + public Task> GetItemsAsync(string directoryPath, CancellationToken cancellationToken) + { + return Task.Run(() => GetItems(directoryPath, cancellationToken), cancellationToken); + } + + // internal for testing + internal ImmutableArray GetItems(string directoryPath, CancellationToken cancellationToken) + { + if (!PathUtilities.IsUnixLikePlatform && directoryPath.Length == 1 && directoryPath[0] == '\\') + { + // The user has typed only "\". In this case, we want to add "\\" to the list. + return ImmutableArray.Create(CreateNetworkRoot()); + } + + var result = ArrayBuilder.GetInstance(); + + var pathKind = PathUtilities.GetPathKind(directoryPath); + switch (pathKind) + { + case PathKind.Empty: + // base directory + if (_baseDirectoryOpt != null) + { + result.AddRange(GetItemsInDirectory(_baseDirectoryOpt, cancellationToken)); + } + + // roots + if (PathUtilities.IsUnixLikePlatform) + { + result.AddRange(CreateUnixRoot()); + } + else + { + foreach (var drive in GetLogicalDrives()) + { + result.Add(CreateLogicalDriveItem(drive.TrimEnd(s_windowsDirectorySeparator))); + } + + result.Add(CreateNetworkRoot()); + } + + // entries on search paths + foreach (var searchPath in _searchPaths) + { + result.AddRange(GetItemsInDirectory(searchPath, cancellationToken)); + } + + break; + + case PathKind.Absolute: + case PathKind.RelativeToCurrentDirectory: + case PathKind.RelativeToCurrentParent: + case PathKind.RelativeToCurrentRoot: + var fullDirectoryPath = FileUtilities.ResolveRelativePath(directoryPath, basePath: null, baseDirectory: _baseDirectoryOpt); + if (fullDirectoryPath != null) + { + result.AddRange(GetItemsInDirectory(fullDirectoryPath, cancellationToken)); + } + else + { + // invalid path + result.Clear(); + } + + break; + + case PathKind.Relative: + + // base directory: + if (_baseDirectoryOpt != null) + { + result.AddRange(GetItemsInDirectory(PathUtilities.CombineAbsoluteAndRelativePaths(_baseDirectoryOpt, directoryPath), cancellationToken)); + } + + // search paths: + foreach (var searchPath in _searchPaths) + { + result.AddRange(GetItemsInDirectory(PathUtilities.CombineAbsoluteAndRelativePaths(searchPath, directoryPath), cancellationToken)); + } + + break; + + case PathKind.RelativeToDriveDirectory: + // Paths "C:dir" are not supported, but when the path doesn't include any directory, i.e. "C:", + // we return the drive itself. + if (directoryPath.Length == 2) + { + result.Add(CreateLogicalDriveItem(directoryPath)); + } + + break; + + default: + throw ExceptionUtilities.UnexpectedValue(pathKind); + } + + return result.ToImmutableAndFree(); + } + + private IEnumerable GetItemsInDirectory(string fullDirectoryPath, CancellationToken cancellationToken) + { + Debug.Assert(PathUtilities.IsAbsolute(fullDirectoryPath)); + + cancellationToken.ThrowIfCancellationRequested(); + + if (!DirectoryExists(fullDirectoryPath)) + { + yield break; + } + + cancellationToken.ThrowIfCancellationRequested(); + + foreach (var directory in EnumerateDirectories(fullDirectoryPath)) + { + if (IsVisibleFileSystemEntry(directory)) + { + yield return CreateFileSystemEntryItem(directory, isDirectory: true); + } + } + + cancellationToken.ThrowIfCancellationRequested(); + + foreach (var file in EnumerateFiles(fullDirectoryPath)) + { + if (_allowableExtensions.Length != 0 && + !_allowableExtensions.Contains( + PathUtilities.GetExtension(file), + PathUtilities.IsUnixLikePlatform ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + cancellationToken.ThrowIfCancellationRequested(); + + if (IsVisibleFileSystemEntry(file)) + { + yield return CreateFileSystemEntryItem(file, isDirectory: false); + } + } + } + } +} diff --git a/src/Features/Core/Portable/Features.csproj b/src/Features/Core/Portable/Features.csproj index 4d5e6720a2477..3b477ba0a3f61 100644 --- a/src/Features/Core/Portable/Features.csproj +++ b/src/Features/Core/Portable/Features.csproj @@ -112,6 +112,7 @@ + diff --git a/src/Interactive/EditorFeatures/CSharp/CSharpInteractiveEditorFeatures.csproj b/src/Interactive/EditorFeatures/CSharp/CSharpInteractiveEditorFeatures.csproj index 4ff1e84c567f4..18f19259486ec 100644 --- a/src/Interactive/EditorFeatures/CSharp/CSharpInteractiveEditorFeatures.csproj +++ b/src/Interactive/EditorFeatures/CSharp/CSharpInteractiveEditorFeatures.csproj @@ -82,6 +82,7 @@ + diff --git a/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/DirectiveCompletionProviderUtilities.cs b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/DirectiveCompletionProviderUtilities.cs new file mode 100644 index 0000000000000..d4d1e71874a98 --- /dev/null +++ b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/DirectiveCompletionProviderUtilities.cs @@ -0,0 +1,32 @@ +// 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; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Extensions; + +namespace Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem +{ + internal static class DirectiveCompletionProviderUtilities + { + internal static bool TryGetStringLiteralToken(SyntaxTree tree, int position, SyntaxKind directiveKind, out SyntaxToken stringLiteral, CancellationToken cancellationToken) + { + if (tree.IsEntirelyWithinStringLiteral(position, cancellationToken)) + { + var token = tree.GetRoot(cancellationToken).FindToken(position, findInsideTrivia: true); + if (token.Kind() == SyntaxKind.EndOfDirectiveToken || token.Kind() == SyntaxKind.EndOfFileToken) + { + token = token.GetPreviousToken(includeSkipped: true, includeDirectives: true); + } + + if (token.Kind() == SyntaxKind.StringLiteralToken && token.Parent.Kind() == directiveKind) + { + stringLiteral = token; + return true; + } + } + + stringLiteral = default(SyntaxToken); + return false; + } + } +} diff --git a/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/LoadDirectiveCompletionProvider.cs b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/LoadDirectiveCompletionProvider.cs index 8c3c2e7c59a0a..fc97127aee53a 100644 --- a/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/LoadDirectiveCompletionProvider.cs +++ b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/LoadDirectiveCompletionProvider.cs @@ -1,13 +1,8 @@ // 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.Text.RegularExpressions; using System.Threading; -using System.Threading.Tasks; -using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; -using Microsoft.CodeAnalysis.Options; -using Microsoft.CodeAnalysis.Text; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Editor.Completion.FileSystem; using Microsoft.VisualStudio.InteractiveWindow; using Microsoft.VisualStudio.Text.Editor; @@ -18,110 +13,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem // regular C# contexts. We will need to remove this and implement a new "CSharp Script" Content type // in order to fix #load completion in .csx files (https://github.com/dotnet/roslyn/issues/5325). [TextViewRole(PredefinedInteractiveTextViewRoles.InteractiveTextViewRole)] - internal partial class LoadDirectiveCompletionProvider : CommonCompletionProvider + internal sealed class LoadDirectiveCompletionProvider : AbstractLoadDirectiveCompletionProvider { - private const string NetworkPath = "\\\\"; - private static readonly Regex s_directiveRegex = new Regex(@"#load\s+(""[^""]*""?)", RegexOptions.Compiled); - - private static readonly ImmutableArray s_commitRules = - ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '"', '\\', ',')); - - private static readonly CompletionItemRules s_rules = CompletionItemRules.Create(commitCharacterRules: s_commitRules); - - public override async Task ProvideCompletionsAsync(CompletionContext context) - { - var text = await context.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); - var items = GetItems(text, context.Document, context.Position, context.Trigger, context.CancellationToken); - context.AddItems(items); - } - - internal override bool IsInsertionTrigger(SourceText text, int characterPosition, OptionSet options) - { - return PathCompletionUtilities.IsTriggerCharacter(text, characterPosition); - } - - private string GetPathThroughLastSlash(SourceText text, int position, Group quotedPathGroup) - { - return PathCompletionUtilities.GetPathThroughLastSlash( - quotedPath: quotedPathGroup.Value, - quotedPathStart: GetQuotedPathStart(text, position, quotedPathGroup), - position: position); - } - - private TextSpan GetTextChangeSpan(SourceText text, int position, Group quotedPathGroup) - { - return PathCompletionUtilities.GetTextChangeSpan( - quotedPath: quotedPathGroup.Value, - quotedPathStart: GetQuotedPathStart(text, position, quotedPathGroup), - position: position); - } - - private static int GetQuotedPathStart(SourceText text, int position, Group quotedPathGroup) - { - return text.Lines.GetLineFromPosition(position).Start + quotedPathGroup.Index; - } - - private ImmutableArray GetItems(SourceText text, Document document, int position, CompletionTrigger triggerInfo, CancellationToken cancellationToken) - { - var line = text.Lines.GetLineFromPosition(position); - var lineText = text.ToString(TextSpan.FromBounds(line.Start, position)); - var match = s_directiveRegex.Match(lineText); - if (!match.Success) - { - return ImmutableArray.Empty; - } - - var quotedPathGroup = match.Groups[1]; - var quotedPath = quotedPathGroup.Value; - var endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPath); - if (endsWithQuote && (position >= line.Start + match.Length)) - { - return ImmutableArray.Empty; - } - - var buffer = text.Container.GetTextBuffer(); - var snapshot = text.FindCorrespondingEditorTextSnapshot(); - if (snapshot == null) - { - return ImmutableArray.Empty; - } - - var fileSystem = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot); - - // TODO: https://github.com/dotnet/roslyn/issues/5263 - // Avoid dependency on a specific resolver. - // The search paths should be provided by specialized workspaces: - // - InteractiveWorkspace for interactive window - // - ScriptWorkspace for loose .csx files (we don't have such workspace today) - var searchPaths = (document.Project.CompilationOptions.SourceReferenceResolver as SourceFileResolver)?.SearchPaths ?? ImmutableArray.Empty; - - var helper = new FileSystemCompletionHelper( - this, - GetTextChangeSpan(text, position, quotedPathGroup), - fileSystem, - Glyph.OpenFolder, - Glyph.CSharpFile, - searchPaths: searchPaths, - allowableExtensions: new[] { ".csx" }, - itemRules: s_rules); - - var pathThroughLastSlash = this.GetPathThroughLastSlash(text, position, quotedPathGroup); - - return helper.GetItems(pathThroughLastSlash, documentPath: null); - } - - protected override Task GetTextChangeAsync(CompletionItem selectedItem, char? ch, CancellationToken cancellationToken) - { - // When we commit "\\" when the user types \ we have to adjust for the fact that the - // controller will automatically append \ after we commit. Because of that, we don't - // want to actually commit "\\" as we'll end up with "\\\". So instead we just commit - // "\" and know that controller will append "\" and give us "\\". - if (selectedItem.DisplayText == NetworkPath && ch == '\\') - { - return Task.FromResult(new TextChange(selectedItem.Span, "\\")); - } - - return base.GetTextChangeAsync(selectedItem, ch, cancellationToken); - } + protected override bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral, CancellationToken cancellationToken) + => DirectiveCompletionProviderUtilities.TryGetStringLiteralToken(tree, position, SyntaxKind.LoadDirectiveTrivia, out stringLiteral, cancellationToken); } } \ No newline at end of file diff --git a/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/ReferenceDirectiveCompletionProvider.cs b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/ReferenceDirectiveCompletionProvider.cs index eabb0e5b7aadf..0404cf37b04c9 100644 --- a/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/ReferenceDirectiveCompletionProvider.cs +++ b/src/Interactive/EditorFeatures/CSharp/Completion/FileSystem/ReferenceDirectiveCompletionProvider.cs @@ -2,7 +2,6 @@ using System.Threading; using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Extensions; using Microsoft.CodeAnalysis.Editor.Completion.FileSystem; using Microsoft.VisualStudio.InteractiveWindow; using Microsoft.VisualStudio.Text.Editor; @@ -15,28 +14,9 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.Completion.FileSystem // regular C# contexts. We will need to remove this and implement a new "CSharp Script" Content type // in order to fix #r completion in .csx files (https://github.com/dotnet/roslyn/issues/5325). [TextViewRole(PredefinedInteractiveTextViewRoles.InteractiveTextViewRole)] - internal partial class ReferenceDirectiveCompletionProvider : AbstractReferenceDirectiveCompletionProvider + internal sealed class ReferenceDirectiveCompletionProvider : AbstractReferenceDirectiveCompletionProvider { - protected override bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral, CancellationToken cancellationToken) - { - if (tree.IsEntirelyWithinStringLiteral(position, cancellationToken)) - { - var token = tree.GetRoot(cancellationToken).FindToken(position, findInsideTrivia: true); - if (token.Kind() == SyntaxKind.EndOfDirectiveToken || token.Kind() == SyntaxKind.EndOfFileToken) - { - token = token.GetPreviousToken(includeSkipped: true, includeDirectives: true); - } - - if (token.Kind() == SyntaxKind.StringLiteralToken && - token.Parent.Kind() == SyntaxKind.ReferenceDirectiveTrivia) - { - stringLiteral = token; - return true; - } - } - - stringLiteral = default(SyntaxToken); - return false; - } + protected override bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral, CancellationToken cancellationToken) => + DirectiveCompletionProviderUtilities.TryGetStringLiteralToken(tree, position, SyntaxKind.ReferenceDirectiveTrivia, out stringLiteral, cancellationToken); } } diff --git a/src/Interactive/EditorFeatures/Core/Completion/AbstractDirectivePathCompletionProvider.cs b/src/Interactive/EditorFeatures/Core/Completion/AbstractDirectivePathCompletionProvider.cs new file mode 100644 index 0000000000000..486597b9b39c9 --- /dev/null +++ b/src/Interactive/EditorFeatures/Core/Completion/AbstractDirectivePathCompletionProvider.cs @@ -0,0 +1,143 @@ +// 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.Completion; +using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; +using Microsoft.CodeAnalysis.Options; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Completion.FileSystem +{ + internal abstract class AbstractDirectivePathCompletionProvider : CompletionProvider + { + protected static bool IsDirectorySeparator(char ch) => + ch == '/' || (ch == '\\' && !PathUtilities.IsUnixLikePlatform); + + protected abstract bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral, CancellationToken cancellationToken); + + public sealed override async Task ProvideCompletionsAsync(CompletionContext context) + { + var document = context.Document; + var position = context.Position; + var cancellationToken = context.CancellationToken; + + var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + + if (!TryGetStringLiteralToken(tree, position, out var stringLiteral, cancellationToken)) + { + return; + } + + var literalValue = stringLiteral.ToString(); + + context.CompletionListSpan = GetTextChangeSpan( + quotedPath: literalValue, + quotedPathStart: stringLiteral.SpanStart, + position: position); + + var pathThroughLastSlash = GetPathThroughLastSlash( + quotedPath: literalValue, + quotedPathStart: stringLiteral.SpanStart, + position: position); + + await ProvideCompletionsAsync(context, pathThroughLastSlash).ConfigureAwait(false); + } + + public override bool ShouldTriggerCompletion(SourceText text, int caretPosition, CompletionTrigger trigger, OptionSet options) + { + return true; + } + + private static string GetPathThroughLastSlash(string quotedPath, int quotedPathStart, int position) + { + Contract.ThrowIfTrue(quotedPath[0] != '"'); + + const int QuoteLength = 1; + + var positionInQuotedPath = position - quotedPathStart; + var path = quotedPath.Substring(QuoteLength, positionInQuotedPath - QuoteLength).Trim(); + var afterLastSlashIndex = AfterLastSlashIndex(path, path.Length); + + // We want the portion up to, and including the last slash if there is one. That way if + // the user pops up completion in the middle of a path (i.e. "C:\Win") then we'll + // consider the path to be "C:\" and we will show appropriate completions. + return afterLastSlashIndex >= 0 ? path.Substring(0, afterLastSlashIndex) : path; + } + + private static TextSpan GetTextChangeSpan(string quotedPath, int quotedPathStart, int position) + { + // We want the text change to be from after the last slash to the end of the quoted + // path. If there is no last slash, then we want it from right after the start quote + // character. + var positionInQuotedPath = position - quotedPathStart; + + // Where we want to start tracking is right after the slash (if we have one), or else + // right after the string starts. + var afterLastSlashIndex = AfterLastSlashIndex(quotedPath, positionInQuotedPath); + var afterFirstQuote = 1; + + var startIndex = Math.Max(afterLastSlashIndex, afterFirstQuote); + var endIndex = quotedPath.Length; + + // If the string ends with a quote, the we do not want to consume that. + if (EndsWithQuote(quotedPath)) + { + endIndex--; + } + + return TextSpan.FromBounds(startIndex + quotedPathStart, endIndex + quotedPathStart); + } + + private static bool EndsWithQuote(string quotedPath) + { + return quotedPath.Length >= 2 && quotedPath[quotedPath.Length - 1] == '"'; + } + + /// + /// Returns the index right after the last slash that precedes 'position'. If there is no + /// slash in the string, -1 is returned. + /// + private static int AfterLastSlashIndex(string text, int position) + { + // Position might be out of bounds of the string (if the string is unterminated. Make + // sure it's within bounds. + position = Math.Min(position, text.Length - 1); + + int index; + if ((index = text.LastIndexOf('/', position)) >= 0 || + !PathUtilities.IsUnixLikePlatform && (index = text.LastIndexOf('\\', position)) >= 0) + { + return index + 1; + } + + return -1; + } + + protected abstract Task ProvideCompletionsAsync(CompletionContext context, string pathThroughLastSlash); + + internal static string GetBaseDirectory(SourceText text, Document document) + { + string result; + if (document.Project.IsSubmission) + { + var buffer = text.Container.GetTextBuffer(); + var snapshot = text.FindCorrespondingEditorTextSnapshot(); + if (snapshot == null) + { + return null; + } + + result = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot).WorkingDirectory; + } + else + { + result = PathUtilities.GetDirectoryName(document.FilePath); + } + + return PathUtilities.IsAbsolute(result) ? result : null; + } + } +} diff --git a/src/Interactive/EditorFeatures/Core/Completion/AbstractLoadDirectiveCompletionProvider.cs b/src/Interactive/EditorFeatures/Core/Completion/AbstractLoadDirectiveCompletionProvider.cs new file mode 100644 index 0000000000000..8119c107c6dd0 --- /dev/null +++ b/src/Interactive/EditorFeatures/Core/Completion/AbstractLoadDirectiveCompletionProvider.cs @@ -0,0 +1,61 @@ +// 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.Tasks; +using Microsoft.CodeAnalysis.Completion; +using Microsoft.CodeAnalysis.Text; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.Editor.Completion.FileSystem +{ + internal abstract class AbstractLoadDirectiveCompletionProvider : AbstractDirectivePathCompletionProvider + { + private static readonly CompletionItemRules s_rules = CompletionItemRules.Create( + filterCharacterRules: ImmutableArray.Empty, + commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, GetCommitCharacters())), + enterKeyRule: EnterKeyRule.Never, + selectionBehavior: CompletionItemSelectionBehavior.HardSelection); + + private static ImmutableArray GetCommitCharacters() + { + var builder = ArrayBuilder.GetInstance(); + builder.Add('"'); + if (PathUtilities.IsUnixLikePlatform) + { + builder.Add('/'); + } + else + { + builder.Add('/'); + builder.Add('\\'); + } + + return builder.ToImmutableAndFree(); + } + + private FileSystemCompletionHelper GetFileSystemCompletionHelper(SourceText text, Document document) + { + // TODO: https://github.com/dotnet/roslyn/issues/5263 + // Avoid dependency on a specific resolver. + // The search paths should be provided by specialized workspaces: + // - InteractiveWorkspace for interactive window + // - MiscFilesWorkspace for loose .csx files + var searchPaths = (document.Project.CompilationOptions.SourceReferenceResolver as SourceFileResolver)?.SearchPaths ?? ImmutableArray.Empty; + + return new FileSystemCompletionHelper( + Glyph.OpenFolder, + Glyph.CSharpFile, + searchPaths: searchPaths, + baseDirectoryOpt: GetBaseDirectory(text, document), + allowableExtensions: ImmutableArray.Create(".csx"), + itemRules: s_rules); + } + + protected override async Task ProvideCompletionsAsync(CompletionContext context, string pathThroughLastSlash) + { + var text = await context.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); + var helper = GetFileSystemCompletionHelper(text, context.Document); + context.AddItems(await helper.GetItemsAsync(pathThroughLastSlash, context.CancellationToken).ConfigureAwait(false)); + } + } +} \ No newline at end of file diff --git a/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs b/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs index d8962aea2ec56..68b05b05d7bf0 100644 --- a/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs +++ b/src/Interactive/EditorFeatures/Core/Completion/AbstractReferenceDirectiveCompletionProvider.cs @@ -1,124 +1,97 @@ // 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.Immutable; -using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; using Microsoft.CodeAnalysis.Host; -using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Scripting.Hosting; using Microsoft.CodeAnalysis.Text; -using Microsoft.VisualStudio.Text; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Editor.Completion.FileSystem { - internal abstract partial class AbstractReferenceDirectiveCompletionProvider : CommonCompletionProvider + internal abstract class AbstractReferenceDirectiveCompletionProvider : AbstractDirectivePathCompletionProvider { - protected abstract bool TryGetStringLiteralToken(SyntaxTree tree, int position, out SyntaxToken stringLiteral, CancellationToken cancellationToken); - - internal override bool IsInsertionTrigger(SourceText text, int characterPosition, OptionSet options) - { - return PathCompletionUtilities.IsTriggerCharacter(text, characterPosition); - } - - private TextSpan GetTextChangeSpan(SyntaxToken stringLiteral, int position) - { - return PathCompletionUtilities.GetTextChangeSpan( - quotedPath: stringLiteral.ToString(), - quotedPathStart: stringLiteral.SpanStart, - position: position); - } - - private static ICurrentWorkingDirectoryDiscoveryService GetFileSystemDiscoveryService(ITextSnapshot textSnapshot) - { - return CurrentWorkingDirectoryDiscoveryService.GetService(textSnapshot); - } - - private static readonly ImmutableArray s_commitRules = - ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, '"', '\\', ',')); - - private static readonly ImmutableArray s_filterRules = ImmutableArray.Empty; - private static readonly CompletionItemRules s_rules = CompletionItemRules.Create( - filterCharacterRules: s_filterRules, commitCharacterRules: s_commitRules, enterKeyRule: EnterKeyRule.Never); + filterCharacterRules: ImmutableArray.Empty, + commitCharacterRules: ImmutableArray.Create(CharacterSetModificationRule.Create(CharacterSetModificationKind.Replace, GetCommitCharacters())), + enterKeyRule: EnterKeyRule.Never, + selectionBehavior: CompletionItemSelectionBehavior.HardSelection); - public override async Task ProvideCompletionsAsync(CompletionContext context) + private static readonly char[] s_pathIndicators = new char[] { '/', '\\', ':' }; + + private static ImmutableArray GetCommitCharacters() { - var document = context.Document; - var position = context.Position; - var cancellationToken = context.CancellationToken; + var builder = ArrayBuilder.GetInstance(); - var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); + builder.Add('"'); - // first try to get the #r string literal token. If we couldn't, then we're not in a #r - // reference directive and we immediately bail. - SyntaxToken stringLiteral; - if (!TryGetStringLiteralToken(tree, position, out stringLiteral, cancellationToken)) + if (PathUtilities.IsUnixLikePlatform) { - return; + builder.Add('/'); + } + else + { + builder.Add('/'); + builder.Add('\\'); } - var textChangeSpan = this.GetTextChangeSpan(stringLiteral, position); - - var gacHelper = new GlobalAssemblyCacheCompletionHelper(this, textChangeSpan, itemRules: s_rules); - var text = await document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); - var snapshot = text.FindCorrespondingEditorTextSnapshot(); - if (snapshot == null) + if (GacFileResolver.IsAvailable) { - // Passing null to GetFileSystemDiscoveryService raises an exception. - // Instead, return here since there is no longer snapshot for this document. - return; + builder.Add(','); } - var referenceResolver = document.Project.CompilationOptions.MetadataReferenceResolver; + return builder.ToImmutableAndFree(); + } + private FileSystemCompletionHelper GetFileSystemCompletionHelper(SourceText text, Document document) + { + var referenceResolver = document.Project.CompilationOptions.MetadataReferenceResolver; + // TODO: https://github.com/dotnet/roslyn/issues/5263 // Avoid dependency on a specific resolvers. // The search paths should be provided by specialized workspaces: // - InteractiveWorkspace for interactive window - // - ScriptWorkspace for loose .csx files (we don't have such workspace today) + // - MiscFilesWorkspace for loose .csx files ImmutableArray searchPaths; - RuntimeMetadataReferenceResolver rtResolver; - WorkspaceMetadataFileReferenceResolver workspaceResolver; - - if ((rtResolver = referenceResolver as RuntimeMetadataReferenceResolver) != null) + if (referenceResolver is RuntimeMetadataReferenceResolver rtResolver) { searchPaths = rtResolver.PathResolver.SearchPaths; } - else if ((workspaceResolver = referenceResolver as WorkspaceMetadataFileReferenceResolver) != null) + else if (referenceResolver is WorkspaceMetadataFileReferenceResolver workspaceResolver) { searchPaths = workspaceResolver.PathResolver.SearchPaths; } else { - return; + searchPaths = ImmutableArray.Empty; } - var fileSystemHelper = new FileSystemCompletionHelper( - this, textChangeSpan, - GetFileSystemDiscoveryService(snapshot), + return new FileSystemCompletionHelper( Glyph.OpenFolder, Glyph.Assembly, searchPaths: searchPaths, - allowableExtensions: new[] { ".dll", ".exe" }, - exclude: path => path.Contains(","), + baseDirectoryOpt: GetBaseDirectory(text, document), + allowableExtensions: ImmutableArray.Create(".dll", ".exe"), itemRules: s_rules); - - var pathThroughLastSlash = GetPathThroughLastSlash(stringLiteral, position); - - var documentPath = document.Project.IsSubmission ? null : document.FilePath; - context.AddItems(gacHelper.GetItems(pathThroughLastSlash, documentPath)); - context.AddItems(fileSystemHelper.GetItems(pathThroughLastSlash, documentPath)); } - private static string GetPathThroughLastSlash(SyntaxToken stringLiteral, int position) + protected override async Task ProvideCompletionsAsync(CompletionContext context, string pathThroughLastSlash) { - return PathCompletionUtilities.GetPathThroughLastSlash( - quotedPath: stringLiteral.ToString(), - quotedPathStart: stringLiteral.SpanStart, - position: position); + if (GacFileResolver.IsAvailable && pathThroughLastSlash.IndexOfAny(s_pathIndicators) < 0) + { + var gacHelper = new GlobalAssemblyCacheCompletionHelper(s_rules); + context.AddItems(await gacHelper.GetItemsAsync(pathThroughLastSlash, context.CancellationToken).ConfigureAwait(false)); + } + + if (pathThroughLastSlash.IndexOf(',') < 0) + { + var text = await context.Document.GetTextAsync(context.CancellationToken).ConfigureAwait(false); + var fileSystemHelper = GetFileSystemCompletionHelper(text, context.Document); + context.AddItems(await fileSystemHelper.GetItemsAsync(pathThroughLastSlash, context.CancellationToken).ConfigureAwait(false)); + } } } } diff --git a/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs b/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs index df640acd029e8..e96469b0a4e55 100644 --- a/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs +++ b/src/Interactive/EditorFeatures/Core/Completion/GlobalAssemblyCacheCompletionHelper.cs @@ -2,15 +2,15 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion; -using Microsoft.CodeAnalysis.Editor.Implementation.IntelliSense.Completion.FileSystem; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; -using Microsoft.CodeAnalysis.Text; using Roslyn.Utilities; -using System.Collections.Immutable; namespace Microsoft.CodeAnalysis.Editor.Completion.FileSystem { @@ -18,55 +18,49 @@ internal sealed class GlobalAssemblyCacheCompletionHelper { private static readonly Lazy> s_lazyAssemblySimpleNames = new Lazy>(() => GlobalAssemblyCache.Instance.GetAssemblySimpleNames().ToList()); - private readonly CompletionProvider _completionProvider; - private readonly TextSpan _textChangeSpan; + private readonly CompletionItemRules _itemRules; - public GlobalAssemblyCacheCompletionHelper( - CompletionProvider completionProvider, - TextSpan textChangeSpan, - CompletionItemRules itemRules = null) + public GlobalAssemblyCacheCompletionHelper(CompletionItemRules itemRules) { - _completionProvider = completionProvider; - _textChangeSpan = textChangeSpan; + Debug.Assert(itemRules != null); _itemRules = itemRules; } - public IEnumerable GetItems(string pathSoFar, string documentPath) + public Task> GetItemsAsync(string directoryPath, CancellationToken cancellationToken) { - var containsSlash = pathSoFar.Contains(@"/") || pathSoFar.Contains(@"\"); - if (containsSlash) - { - return SpecializedCollections.EmptyEnumerable(); - } - - return GetCompletionsWorker(pathSoFar).ToList(); + return Task.Run(() => GetItems(directoryPath, cancellationToken)); } - private IEnumerable GetCompletionsWorker(string pathSoFar) + // internal for testing + internal ImmutableArray GetItems(string directoryPath, CancellationToken cancellationToken) { - var comma = pathSoFar.IndexOf(','); + var result = ArrayBuilder.GetInstance(); + + var comma = directoryPath.IndexOf(','); if (comma >= 0) { - var path = pathSoFar.Substring(0, comma); - return from identity in GetAssemblyIdentities(path) - let text = identity.GetDisplayName() - select CommonCompletionItem.Create(text, glyph: Glyph.Assembly, rules: _itemRules); + var partialName = directoryPath.Substring(0, comma); + foreach (var identity in GetAssemblyIdentities(partialName)) + { + result.Add(CommonCompletionItem.Create(identity.GetDisplayName(), glyph: Glyph.Assembly, rules: _itemRules)); + } } else { - return from displayName in s_lazyAssemblySimpleNames.Value - select CommonCompletionItem.Create( - displayName, - description: GlobalAssemblyCache.Instance.ResolvePartialName(displayName).GetDisplayName().ToSymbolDisplayParts(), - glyph: Glyph.Assembly, - rules: _itemRules); + foreach (var displayName in s_lazyAssemblySimpleNames.Value) + { + cancellationToken.ThrowIfCancellationRequested(); + result.Add(CommonCompletionItem.Create(displayName, glyph: Glyph.Assembly, rules: _itemRules)); + } } + + return result.ToImmutableAndFree(); } - private IEnumerable GetAssemblyIdentities(string pathSoFar) + private IEnumerable GetAssemblyIdentities(string partialName) { - return IOUtilities.PerformIO(() => GlobalAssemblyCache.Instance.GetAssemblyIdentities(pathSoFar), + return IOUtilities.PerformIO(() => GlobalAssemblyCache.Instance.GetAssemblyIdentities(partialName), SpecializedCollections.EmptyEnumerable()); } } diff --git a/src/Interactive/EditorFeatures/Core/InteractiveEditorFeatures.csproj b/src/Interactive/EditorFeatures/Core/InteractiveEditorFeatures.csproj index 943854a9aabc3..6c17643941924 100644 --- a/src/Interactive/EditorFeatures/Core/InteractiveEditorFeatures.csproj +++ b/src/Interactive/EditorFeatures/Core/InteractiveEditorFeatures.csproj @@ -105,9 +105,11 @@ + + diff --git a/src/Interactive/EditorFeatures/VisualBasic/BasicInteractiveEditorFeatures.vbproj b/src/Interactive/EditorFeatures/VisualBasic/BasicInteractiveEditorFeatures.vbproj index d5df24ac7354a..1b687dd026684 100644 --- a/src/Interactive/EditorFeatures/VisualBasic/BasicInteractiveEditorFeatures.vbproj +++ b/src/Interactive/EditorFeatures/VisualBasic/BasicInteractiveEditorFeatures.vbproj @@ -67,6 +67,8 @@ + + diff --git a/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/DirectiveCompletionProviderUtilities.vb b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/DirectiveCompletionProviderUtilities.vb new file mode 100644 index 0000000000000..95a4f395edd53 --- /dev/null +++ b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/DirectiveCompletionProviderUtilities.vb @@ -0,0 +1,23 @@ +' 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.Threading +Imports Microsoft.CodeAnalysis + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion.CompletionProviders + Friend Module DirectiveCompletionProviderUtilities + Friend Function TryGetStringLiteralToken(tree As SyntaxTree, position As Integer, directiveKind As SyntaxKind, ByRef stringLiteral As SyntaxToken, cancellationToken As CancellationToken) As Boolean + If tree.IsEntirelyWithinStringLiteral(position, cancellationToken) Then + Dim token = tree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives:=True, includeDocumentationComments:=True) + + ' Verifies that the string literal under caret is the path token. + If token.IsKind(SyntaxKind.StringLiteralToken) AndAlso token.Parent.IsKind(directiveKind) Then + stringLiteral = token + Return True + End If + End If + + stringLiteral = Nothing + Return False + End Function + End Module +End Namespace \ No newline at end of file diff --git a/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/LoadDirectiveCompletionProvider.vb b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/LoadDirectiveCompletionProvider.vb new file mode 100644 index 0000000000000..1f62f3c6fd525 --- /dev/null +++ b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/LoadDirectiveCompletionProvider.vb @@ -0,0 +1,19 @@ +' 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.Threading +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Editor.Completion.FileSystem +Imports Microsoft.VisualStudio.InteractiveWindow +Imports Microsoft.VisualStudio.Text.Editor + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion.CompletionProviders +#If TODO Then ' VB doesn't have LoadDirectiveTrivia defined yet + + + Friend NotInheritable Class LoadDirectiveCompletionProvider : Inherits AbstractReferenceDirectiveCompletionProvider + Protected Overrides Function TryGetStringLiteralToken(tree As SyntaxTree, position As Integer, ByRef stringLiteral As SyntaxToken, cancellationToken As CancellationToken) As Boolean + Return DirectiveCompletionProviderUtilities.TryGetStringLiteralToken(tree, position, SyntaxKind.LoadDirectiveTrivia, stringLiteral, cancellationToken) + End Function + End Class +#End If +End Namespace \ No newline at end of file diff --git a/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/ReferenceDirectiveCompletionProvider.vb b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/ReferenceDirectiveCompletionProvider.vb index 0c9abadae68bb..a6f82b2d32165 100644 --- a/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/ReferenceDirectiveCompletionProvider.vb +++ b/src/Interactive/EditorFeatures/VisualBasic/Interactive/FileSystem/ReferenceDirectiveCompletionProvider.vb @@ -12,18 +12,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.Completion.CompletionProvide Friend Class ReferenceDirectiveCompletionProvider : Inherits AbstractReferenceDirectiveCompletionProvider Protected Overrides Function TryGetStringLiteralToken(tree As SyntaxTree, position As Integer, ByRef stringLiteral As SyntaxToken, cancellationToken As CancellationToken) As Boolean - If tree.IsEntirelyWithinStringLiteral(position, cancellationToken) Then - Dim token = tree.FindTokenOnLeftOfPosition(position, cancellationToken, includeDirectives:=True, includeDocumentationComments:=True) - - ' Verifies that the string literal under caret is the path token. - If token.IsKind(SyntaxKind.StringLiteralToken) AndAlso token.Parent.IsKind(SyntaxKind.ReferenceDirectiveTrivia) Then - stringLiteral = token - Return True - End If - End If - - stringLiteral = Nothing - Return False + Return DirectiveCompletionProviderUtilities.TryGetStringLiteralToken(tree, position, SyntaxKind.ReferenceDirectiveTrivia, stringLiteral, cancellationToken) End Function End Class diff --git a/src/Workspaces/Core/Portable/Utilities/StringEscapeEncoder.cs b/src/Workspaces/Core/Portable/Utilities/StringEscapeEncoder.cs index 02e3d0655fdd5..a139d10249530 100644 --- a/src/Workspaces/Core/Portable/Utilities/StringEscapeEncoder.cs +++ b/src/Workspaces/Core/Portable/Utilities/StringEscapeEncoder.cs @@ -16,9 +16,9 @@ public static string Escape(this string text, char escapePrefix, params char[] p { var prefixIndex = text.IndexOf(escapePrefix, startIndex); var prohibitIndex = text.IndexOfAny(prohibitedCharacters, startIndex); - var index = prefixIndex > 0 && prohibitIndex > 0 ? Math.Min(prefixIndex, prohibitIndex) - : prefixIndex > 0 ? prefixIndex - : prohibitIndex > 0 ? prohibitIndex + var index = prefixIndex >= 0 && prohibitIndex >= 0 ? Math.Min(prefixIndex, prohibitIndex) + : prefixIndex >= 0 ? prefixIndex + : prohibitIndex >= 0 ? prohibitIndex : -1; if (index < 0) diff --git a/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs b/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs index 84dfe9bd4228b..6d7778f845c5f 100644 --- a/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs +++ b/src/Workspaces/CoreTest/UtilityTest/StringEscapingTests.cs @@ -1,8 +1,5 @@ // 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.IO; -using System.Threading; using Roslyn.Utilities; using Xunit; @@ -17,6 +14,7 @@ public void TestEscaping() Assert.Equal($"abc${(int)'?':X2}", "abc?".Escape('$', '?')); Assert.Equal($"abc${(int)'$':X2}", "abc$".Escape('$', '?')); Assert.Equal($"abc${(int)'?':X2}def${(int)'!':X2}", "abc?def!".Escape('$', '?', '!')); + Assert.Equal($"${(int)'?':X2}${(int)'!':X2}ab", "?!ab".Escape('$', '?', '!')); } [Fact] @@ -26,6 +24,7 @@ public void TestUnescaping() Assert.Equal("abc?", $"abc${(int)'?':X2}".Unescape('$')); Assert.Equal("abc$", $"abc${(int)'$':X2}".Unescape('$')); Assert.Equal("abc?def!", $"abc${(int)'?':X2}def${(int)'!':X2}".Unescape('$')); + Assert.Equal("?!ab", $"${(int)'?':X2}${(int)'!':X2}ab".Unescape('$')); } } } \ No newline at end of file From 1ee15f02cbfaaf785b4e45c29157c682c2654266 Mon Sep 17 00:00:00 2001 From: Shyam N Date: Thu, 4 May 2017 01:27:26 -0700 Subject: [PATCH 162/214] Fix C# partial method instrumentation. --- .../CSharp/Portable/CodeGen/EmitExpression.cs | 12 +- .../DynamicInstrumentationTests.cs | 176 +++++++++++++++++- 2 files changed, 185 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs index 1c2d421f538fe..4491e24aaf547 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/EmitExpression.cs @@ -2719,7 +2719,17 @@ private void EmitMethodDefIndexExpression(BoundMethodDefIndex node) Debug.Assert(node.Method.IsDefinition); Debug.Assert(node.Type.SpecialType == SpecialType.System_Int32); _builder.EmitOpCode(ILOpCode.Ldtoken); - EmitSymbolToken(node.Method, node.Syntax, null, encodeAsRawDefinitionToken: true); + + // For partial methods, we emit pseudo token based on the symbol for the partial + // definition part as opposed to the symbol for the partial implementation part. + // We will need to resolve the symbol associated with each pseudo token in order + // to compute the real method definition tokens later. For partial methods, this + // resolution can only succeed if the associated symbol is the symbol for the + // partial definition and not the symbol for the partial implementation (see + // MethodSymbol.ResolvedMethodImpl()). + var symbol = node.Method.PartialDefinitionPart ?? node.Method; + + EmitSymbolToken(symbol, node.Syntax, null, encodeAsRawDefinitionToken: true); } private void EmitMaximumMethodDefIndexExpression(BoundMaximumMethodDefIndex node) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs index c689f5f5f7553..eed66d111bea2 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.cs @@ -2380,7 +2380,7 @@ int P2 } "; var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll); - + AssertNotInstrumented(verifier, "C.P1.get"); AssertNotInstrumented(verifier, "C.P1.set"); AssertNotInstrumented(verifier, "C.g__L11_0"); @@ -2526,7 +2526,7 @@ event Action E2 { add { } remove { } } } "; var verifier = CompileAndVerify(source + InstrumentationHelperSource, options: TestOptions.ReleaseDll); - + AssertNotInstrumented(verifier, "C.P1.get"); AssertNotInstrumented(verifier, "C.P1.set"); AssertNotInstrumented(verifier, "C.E1.add"); @@ -2612,6 +2612,178 @@ void M() {} AssertInstrumented(verifier, "D.M"); } + [Fact] + public void TestPartialMethodsWithImplementation() + { + var source = @" +using System; + +public partial class Class1 +{ + partial void Method1(int x); + public void Method2(int x) + { + Console.WriteLine($""Method2: x = {x}""); + Method1(x); + } +} + +public partial class Class1 +{ + partial void Method1(int x) + { + Console.WriteLine($""Method1: x = {x}""); + if (x > 0) + { + Console.WriteLine(""Method1: x > 0""); + Method1(0); + } + else if (x < 0) + { + Console.WriteLine(""Method1: x < 0""); + } + } +} + +public class Program +{ + public static void Main(string[] args) + { + Test(); + Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); + } + + static void Test() + { + Console.WriteLine(""Test""); + var c = new Class1(); + c.Method2(1); + } +} +" + InstrumentationHelperSource; + + var checker = new CSharpInstrumentationChecker(); + checker.Method(1, 1, "partial void Method1(int x)") + .True(@"Console.WriteLine($""Method1: x = {x}"");") + .True(@"Console.WriteLine(""Method1: x > 0"");") + .True("Method1(0);") + .False(@"Console.WriteLine(""Method1: x < 0"");") + .True("x < 0)") + .True("x > 0)"); + checker.Method(2, 1, "public void Method2(int x)") + .True(@"Console.WriteLine($""Method2: x = {x}"");") + .True("Method1(x);"); + checker.Method(3, 1, ".ctor()", expectBodySpan: false); + checker.Method(4, 1, "public static void Main(string[] args)") + .True("Test();") + .True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();"); + checker.Method(5, 1, "static void Test()") + .True(@"Console.WriteLine(""Test"");") + .True("var c = new Class1();") + .True("c.Method2(1);"); + checker.Method(8, 1) + .True() + .False() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True(); + + var expectedOutput = @"Test +Method2: x = 1 +Method1: x = 1 +Method1: x > 0 +Method1: x = 0 +" + checker.ExpectedOutput; + + var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe); + checker.CompleteCheck(verifier.Compilation, source); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe); + checker.CompleteCheck(verifier.Compilation, source); + verifier.VerifyDiagnostics(); + } + + [Fact] + public void TestPartialMethodsWithoutImplementation() + { + var source = @" +using System; + +public partial class Class1 +{ + partial void Method1(int x); + public void Method2(int x) + { + Console.WriteLine($""Method2: x = {x}""); + Method1(x); + } +} + +public class Program +{ + public static void Main(string[] args) + { + Test(); + Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload(); + } + + static void Test() + { + Console.WriteLine(""Test""); + var c = new Class1(); + c.Method2(1); + } +} +" + InstrumentationHelperSource; + + var checker = new CSharpInstrumentationChecker(); + checker.Method(1, 1, "public void Method2(int x)") + .True(@"Console.WriteLine($""Method2: x = {x}"");"); + checker.Method(2, 1, ".ctor()", expectBodySpan: false); + checker.Method(3, 1, "public static void Main(string[] args)") + .True("Test();") + .True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload();"); + checker.Method(4, 1, "static void Test()") + .True(@"Console.WriteLine(""Test"");") + .True("var c = new Class1();") + .True("c.Method2(1);"); + checker.Method(7, 1) + .True() + .False() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True() + .True(); + + var expectedOutput = @"Test +Method2: x = 1 +" + checker.ExpectedOutput; + + var verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.ReleaseExe); + checker.CompleteCheck(verifier.Compilation, source); + verifier.VerifyDiagnostics(); + + verifier = CompileAndVerify(source, expectedOutput, options: TestOptions.DebugExe); + checker.CompleteCheck(verifier.Compilation, source); + verifier.VerifyDiagnostics(); + } + private static void AssertNotInstrumented(CompilationVerifier verifier, string qualifiedMethodName) => AssertInstrumented(verifier, qualifiedMethodName, expected: false); From 38322d4820dfc0777a92760585b8b636f4d5c4f5 Mon Sep 17 00:00:00 2001 From: Shyam N Date: Thu, 4 May 2017 15:05:59 -0700 Subject: [PATCH 163/214] Fix VB partial method instrumentation. --- .../Portable/CodeGen/EmitExpression.vb | 19 +- .../DynamicInstrumentationTests.vb | 177 +++++++++++++++++- 2 files changed, 187 insertions(+), 9 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb b/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb index 0857acb38502c..fa2f527d005ec 100644 --- a/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb +++ b/src/Compilers/VisualBasic/Portable/CodeGen/EmitExpression.vb @@ -1,18 +1,13 @@ ' 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 -Imports System.Collections.Generic Imports System.Collections.Immutable -Imports System.Diagnostics -Imports System.Linq Imports System.Reflection.Metadata Imports Microsoft.CodeAnalysis.CodeGen Imports Microsoft.CodeAnalysis.VisualBasic.Symbols -Imports TypeKind = Microsoft.CodeAnalysis.TypeKind Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen - Friend Partial Class CodeGenerator + Partial Friend Class CodeGenerator Private _recursionDepth As Integer Private Class EmitCancelledException @@ -2205,7 +2200,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGen Debug.Assert(node.Method.IsDefinition) Debug.Assert(node.Type.SpecialType = SpecialType.System_Int32) _builder.EmitOpCode(ILOpCode.Ldtoken) - EmitSymbolToken(node.Method, node.Syntax, encodeAsRawDefinitionToken:=True) + + ' For partial methods, we emit pseudo token based on the symbol for the partial + ' definition part as opposed to the symbol for the partial implementation part. + ' We will need to resolve the symbol associated with each pseudo token in order + ' to compute the real method definition tokens later. For partial methods, this + ' resolution can only succeed if the associated symbol is the symbol for the + ' partial definition and not the symbol for the partial implementation (see + ' MethodSymbol.ResolvedMethodImpl()). + Dim symbol = If(node.Method.PartialDefinitionPart, node.Method) + + EmitSymbolToken(symbol, node.Syntax, encodeAsRawDefinitionToken:=True) End Sub Private Sub EmitMaximumMethodDefIndexExpression(node As BoundMaximumMethodDefIndex) diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.vb index eecd8ec2417cb..89023dcc6d729 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/DynamicAnalysis/DynamicInstrumentationTests.vb @@ -7,8 +7,6 @@ Imports Microsoft.CodeAnalysis.Test.Utilities Imports Microsoft.CodeAnalysis.Test.Utilities.VBInstrumentationChecker Imports Microsoft.CodeAnalysis.VisualBasic Imports Microsoft.CodeAnalysis.VisualBasic.UnitTests -Imports Roslyn.Test.Utilities -Imports Xunit Namespace Microsoft.CodeAnalysis.VisualBasic.DynamicAnalysis.UnitTests @@ -2368,6 +2366,173 @@ End Class AssertInstrumented(verifier, "D.M") End Sub + + Public Sub TestPartialMethodsWithImplementation() + Dim testSource = + 0 + Console.WriteLine("Method1: x > 0") + Method1(0) + ElseIf x < 0 + Console.WriteLine("Method1: x < 0") + End If + End Sub +End Class + +Module Program + Public Sub Main() + Test() + Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload() + End Sub + + Sub Test() + Console.WriteLine("Test") + Dim c = new Class1() + c.Method2(1) + End Sub +End Module +]]> + + + Dim source = + <%= testSource %> + <%= InstrumentationHelperSource %> + + + Dim checker = New VBInstrumentationChecker() + checker.Method(1, 1, "New", expectBodySpan:=False) + checker.Method(2, 1, "Private Sub Method1(x as Integer)"). + True("Console.WriteLine(""Method1: x = {0}"", x)"). + True("Console.WriteLine(""Method1: x > 0"")"). + True("Method1(0)"). + False("Console.WriteLine(""Method1: x < 0"")"). + True("x < 0"). + True("x > 0") + checker.Method(3, 1, "Public Sub Method2(x as Integer)"). + True("Console.WriteLine(""Method2: x = {0}"", x)"). + True("Method1(x)") + checker.Method(4, 1, "Public Sub Main()"). + True("Test()"). + True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()") + checker.Method(5, 1, "Sub Test()"). + True("Console.WriteLine(""Test"")"). + True("new Class1()"). + True("c.Method2(1)") + checker.Method(8, 1). + True(). + False(). + True(). + True(). + True(). + True(). + True(). + True(). + True(). + True(). + True() + + Dim expectedOutput = "Test +Method2: x = 1 +Method1: x = 1 +Method1: x > 0 +Method1: x = 0 +" + XCDataToString(checker.ExpectedOutput) + + Dim verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.ReleaseExe) + checker.CompleteCheck(verifier.Compilation, testSource) + verifier.VerifyDiagnostics() + + verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.DebugExe) + checker.CompleteCheck(verifier.Compilation, testSource) + verifier.VerifyDiagnostics() + End Sub + + + Public Sub TestPartialMethodsWithoutImplementation() + Dim testSource = + + + + Dim source = + <%= testSource %> + <%= InstrumentationHelperSource %> + + + Dim checker = New VBInstrumentationChecker() + checker.Method(1, 1, "New", expectBodySpan:=False) + checker.Method(2, 1, "Public Sub Method2(x as Integer)"). + True("Console.WriteLine(""Method2: x = {0}"", x)") + checker.Method(3, 1, "Public Sub Main()"). + True("Test()"). + True("Microsoft.CodeAnalysis.Runtime.Instrumentation.FlushPayload()") + checker.Method(4, 1, "Sub Test()"). + True("Console.WriteLine(""Test"")"). + True("new Class1()"). + True("c.Method2(1)") + checker.Method(7, 1). + True(). + False(). + True(). + True(). + True(). + True(). + True(). + True(). + True(). + True(). + True() + + Dim expectedOutput = "Test +Method2: x = 1 +" + XCDataToString(checker.ExpectedOutput) + + Dim verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.ReleaseExe) + checker.CompleteCheck(verifier.Compilation, testSource) + verifier.VerifyDiagnostics() + + verifier = CompileAndVerify(source, expectedOutput, options:=TestOptions.DebugExe) + checker.CompleteCheck(verifier.Compilation, testSource) + verifier.VerifyDiagnostics() + End Sub + Private Shared Sub AssertNotInstrumented(verifier As CompilationVerifier, qualifiedMethodName As String) AssertInstrumented(verifier, qualifiedMethodName, expected:=False) End Sub @@ -2394,6 +2559,14 @@ End Class emitOptions:=EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage))) End Function + Private Overloads Function CompileAndVerify(source As XElement, Optional expectedOutput As String = Nothing, Optional options As VisualBasicCompilationOptions = Nothing) As CompilationVerifier + Return CompileAndVerify(source, + LatestVbReferences, + expectedOutput, + options:=If(options, TestOptions.ReleaseExe).WithDeterministic(True), + emitOptions:=EmitOptions.Default.WithInstrumentationKinds(ImmutableArray.Create(InstrumentationKind.TestCoverage))) + End Function + Private Overloads Function CompileAndVerify(source As String, Optional expectedOutput As String = Nothing, Optional options As VisualBasicCompilationOptions = Nothing) As CompilationVerifier Return CompileAndVerify(source, LatestVbReferences, From 8cc7daaccf0f00e4859dd9ef0b4fa1eb82022aa8 Mon Sep 17 00:00:00 2001 From: Shyam N Date: Fri, 5 May 2017 15:03:32 -0700 Subject: [PATCH 164/214] Crash the compiler if we are trying to emit a method definition token but end up with a handle that does not correspond to a method definition. --- .../Core/Portable/PEWriter/MetadataWriter.cs | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs index c65fe050a3595..0ee95b95939fa 100644 --- a/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs +++ b/src/Compilers/Core/Portable/PEWriter/MetadataWriter.cs @@ -95,7 +95,7 @@ protected MetadataWriter( this.Context = context; this.messageProvider = messageProvider; _cancellationToken = cancellationToken; - + this.metadata = metadata; _debugMetadataOpt = debugMetadataOpt; _dynamicAnalysisDataWriterOpt = dynamicAnalysisDataWriterOpt; @@ -322,7 +322,7 @@ private bool IsMinimalDelta /// The greatest index given to any method definition. /// protected abstract int GreatestMethodDefIndex { get; } - + /// /// Return true and full metadata handle of the type reference /// if the reference is available in the current generation. @@ -423,9 +423,9 @@ private bool IsMinimalDelta // Shared builder (reference equals heaps) if we are embedding Portable PDB into the metadata stream. // Null otherwise. protected readonly MetadataBuilder _debugMetadataOpt; - + internal bool EmitStandaloneDebugMetadata => _debugMetadataOpt != null && metadata != _debugMetadataOpt; - + private readonly DynamicAnalysisDataWriter _dynamicAnalysisDataWriterOpt; private readonly Dictionary _customAttributeSignatureIndex = new Dictionary(); @@ -611,8 +611,8 @@ private ImmutableArray GetParametersToEmitCore(IMethodDefi // No explicit param row is needed if param has no flags (other than optionally IN), // no name and no references to the param row, such as CustomAttribute, Constant, or FieldMarshal - if (parDef.Name != String.Empty || - parDef.HasDefaultValue || parDef.IsOptional || parDef.IsOut || parDef.IsMarshalledExplicitly || + if (parDef.Name != String.Empty || + parDef.HasDefaultValue || parDef.IsOptional || parDef.IsOut || parDef.IsMarshalledExplicitly || IteratorHelper.EnumerableIsNotEmpty(parDef.GetAttributes(Context))) { if (builder != null) @@ -675,7 +675,7 @@ private void CreateInitialAssemblyRefIndex() private void CreateInitialFileRefIndex() { Debug.Assert(!_tableIndicesAreComplete); - + foreach (IFileReference fileRef in module.GetFiles(Context)) { string key = fileRef.FileName; @@ -1153,7 +1153,7 @@ private BlobHandle GetMethodSignatureHandleAndBlob(IMethodReference methodRefere var builder = PooledBlobBuilder.GetInstance(); var encoder = new BlobEncoder(builder).MethodSignature( - new SignatureHeader((byte)methodReference.CallingConvention).CallingConvention, + new SignatureHeader((byte)methodReference.CallingConvention).CallingConvention, methodReference.GenericParameterCount, isInstanceMethod: (methodReference.CallingConvention & CallingConvention.HasThis) != 0); @@ -1724,10 +1724,10 @@ public void WriteMetadataAndIL(PdbWriter nativePdbWriterOpt, Stream metadataStre BuildMetadataAndIL( nativePdbWriterOpt, - ilBuilder, + ilBuilder, mappedFieldDataBuilder, managedResourceDataBuilder, - out Blob mvidFixup, + out Blob mvidFixup, out Blob mvidStringFixup); var typeSystemRowCounts = metadata.GetRowCounts(); @@ -1791,14 +1791,14 @@ public void BuildMetadataAndIL( _tableIndicesAreComplete = true; ReportReferencesToAddedSymbols(); - + BlobBuilder dynamicAnalysisDataOpt = null; if (_dynamicAnalysisDataWriterOpt != null) { dynamicAnalysisDataOpt = new BlobBuilder(); _dynamicAnalysisDataWriterOpt.SerializeMetadataTables(dynamicAnalysisDataOpt); } - + PopulateTypeSystemTables(methodBodyOffsets, mappedFieldDataBuilder, managedResourceDataBuilder, dynamicAnalysisDataOpt, out mvidFixup); } @@ -1948,7 +1948,7 @@ private void PopulateAssemblyTableRows() name: GetStringHandleForPathAndCheckLength(module.Name, module), culture: metadata.GetOrAddString(sourceAssembly.Identity.CultureName)); } - + private void PopulateCustomAttributeTableRows(ImmutableArray sortedGenericParameters) { if (this.IsFullMetadata) @@ -2278,9 +2278,9 @@ private void PopulateFieldMarshalTableRows() var marshallingInformation = parDef.MarshallingInformation; - BlobHandle descriptor = (marshallingInformation != null) - ? GetMarshallingDescriptorHandle(marshallingInformation) - : GetMarshallingDescriptorHandle(parDef.MarshallingDescriptor); + BlobHandle descriptor = (marshallingInformation != null) + ? GetMarshallingDescriptorHandle(marshallingInformation) + : GetMarshallingDescriptorHandle(parDef.MarshallingDescriptor); metadata.AddMarshallingDescriptor( parent: GetParameterHandle(parDef), @@ -2450,7 +2450,7 @@ private void PopulateInterfaceImplTableRows() } } } - + private void PopulateManifestResourceTableRows(BlobBuilder resourceDataWriter, BlobBuilder dynamicAnalysisDataOpt) { if (dynamicAnalysisDataOpt != null) @@ -2462,7 +2462,7 @@ private void PopulateManifestResourceTableRows(BlobBuilder resourceDataWriter, B offset: GetManagedResourceOffset(dynamicAnalysisDataOpt, resourceDataWriter) ); } - + foreach (var resource in this.module.GetResources(Context)) { EntityHandle implementation; @@ -2497,11 +2497,11 @@ private void PopulateMemberRefTableRows() { metadata.AddMemberReference( parent: GetMemberReferenceParent(memberRef), - name: GetStringHandleForNameAndCheckLength(memberRef.Name, memberRef), + name: GetStringHandleForNameAndCheckLength(memberRef.Name, memberRef), signature: GetMemberReferenceSignatureHandle(memberRef)); } } - + private void PopulateMethodImplTableRows() { metadata.SetCapacity(TableIndex.MethodImpl, methodImplList.Count); @@ -2514,7 +2514,7 @@ private void PopulateMethodImplTableRows() methodDeclaration: GetMethodDefinitionOrReferenceHandle(methodImplementation.ImplementedMethod)); } } - + private void PopulateMethodSpecTableRows() { var methodSpecs = this.GetMethodSpecs(); @@ -2652,7 +2652,7 @@ private void PopulateModuleTableRow(out Blob mvidFixup) encId: metadata.GetOrAddGuid(EncId), encBaseId: metadata.GetOrAddGuid(EncBaseId)); } - + private void PopulateParamTableRows() { var parameterDefs = this.GetParameterDefs(); @@ -2680,7 +2680,7 @@ private void PopulatePropertyTableRows() signature: GetPropertySignatureHandle(propertyDef)); } } - + private void PopulateTypeDefTableRows() { var typeDefs = this.GetTypeDefs(); @@ -2863,7 +2863,7 @@ private int[] SerializeMethodBodies(BlobBuilder ilBuilder, PdbWriter nativePdbWr } _dynamicAnalysisDataWriterOpt?.SerializeMethodDynamicAnalysisData(body); - + bodyOffsets[methodRid - 1] = bodyOffset; methodRid++; @@ -2891,11 +2891,11 @@ private int SerializeMethodBody(MethodBodyStreamEncoder encoder, IMethodBody met } var encodedBody = encoder.AddMethodBody( - codeSize: methodBody.IL.Length, - maxStack: methodBody.MaxStack, - exceptionRegionCount: exceptionRegions.Length, + codeSize: methodBody.IL.Length, + maxStack: methodBody.MaxStack, + exceptionRegionCount: exceptionRegions.Length, hasSmallExceptionRegions: MayUseSmallExceptionHeaders(exceptionRegions), - localVariablesSignature: localSignatureHandleOpt, + localVariablesSignature: localSignatureHandleOpt, attributes: (methodBody.LocalsAreZeroed ? MethodBodyAttributes.InitLocals : 0)); // Don't do small body method caching during deterministic builds until this issue is fixed @@ -3083,7 +3083,7 @@ private ReservedBlob ReserveUserString(int length) internal const uint LiteralGreatestMethodDefinitionToken = 0x40000000; internal const uint SourceDocumentIndex = 0x20000000; internal const uint ModuleVersionIdStringToken = 0x80000000; - + private void WriteInstructions(Blob finalIL, ImmutableArray generatedIL, ref UserStringHandle mvidStringHandle, ref Blob mvidStringFixup) { // write the raw body first and then patch tokens: @@ -3119,7 +3119,9 @@ private void WriteInstructions(Blob finalIL, ImmutableArray generatedIL, r switch ((uint)tokenMask) { case LiteralMethodDefinitionToken: - token = MetadataTokens.GetToken(ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff)) & 0x00ffffff; + // Crash the compiler if pseudo token fails to resolve to a MethodDefinitionHandle. + var handle = (MethodDefinitionHandle)ResolveEntityHandleFromPseudoToken(pseudoToken & 0x00ffffff); + token = MetadataTokens.GetToken(handle) & 0x00ffffff; break; case LiteralGreatestMethodDefinitionToken: token = GreatestMethodDefIndex; @@ -3137,7 +3139,7 @@ private void WriteInstructions(Blob finalIL, ImmutableArray generatedIL, r offset += 4; break; } - + case OperandType.InlineString: { writer.Offset = offset; @@ -3221,7 +3223,7 @@ private void SerializeMethodBodyExceptionHandlerTable(ExceptionRegionEncoder enc region.TryLength, region.HandlerStartOffset, region.HandlerLength, - (exceptionType != null) ? GetTypeHandle(exceptionType) : default(EntityHandle), + (exceptionType != null) ? GetTypeHandle(exceptionType) : default(EntityHandle), region.FilterDecisionStartOffset); } } @@ -3380,7 +3382,7 @@ private void SerializeMetadataExpression(LiteralEncoder encoder, IMetadataExpres { CustomAttributeElementTypeEncoder typeEncoder; encoder.TaggedScalar(out typeEncoder, out scalarEncoder); - + // special case null argument assigned to Object parameter - treat as null string if (c != null && c.Value == null && @@ -3405,7 +3407,7 @@ private void SerializeMetadataExpression(LiteralEncoder encoder, IMetadataExpres scalarEncoder.NullArray(); return; } - + Debug.Assert(!module.IsPlatformType(c.Type, PlatformType.SystemType) || c.Value == null); scalarEncoder.Constant(c.Value); } @@ -3599,7 +3601,7 @@ private void SerializePermissionSet(IEnumerable permissionSet, private void SerializeReturnValueAndParameters(MethodSignatureEncoder encoder, ISignature signature, ImmutableArray varargParameters) { var declaredParameters = signature.GetParameters(Context); - var returnType = signature.GetType(Context); + var returnType = signature.GetType(Context); ReturnTypeEncoder returnTypeEncoder; ParametersEncoder parametersEncoder; From 4c00cec073687f66dc48c6a7da313b99bb49dc73 Mon Sep 17 00:00:00 2001 From: Neal Gafter Date: Fri, 5 May 2017 15:08:21 -0700 Subject: [PATCH 165/214] Fix a crash in the parser due to ParseStatementCore returning null (#19281) Other call sites appear to have been adapted already. Fixes https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?_a=edit&id=266237 --- .../CSharp/Portable/Parser/LanguageParser.cs | 2 +- .../Syntax/Parsing/StatementParsingTests.cs | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 51099af4c439f..c3f845c305e72 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -8222,7 +8222,7 @@ private LabeledStatementSyntax ParseLabeledStatement() var label = this.ParseIdentifierToken(); var colon = this.EatToken(SyntaxKind.ColonToken); Debug.Assert(!colon.IsMissing); - var statement = this.ParseStatementCore(); + var statement = this.ParseStatementCore() ?? SyntaxFactory.EmptyStatement(EatToken(SyntaxKind.SemicolonToken)); return _syntaxFactory.LabeledStatement(label, colon, statement); } diff --git a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs index 61105c0f14c16..b695a369406e8 100644 --- a/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs +++ b/src/Compilers/CSharp/Test/Syntax/Parsing/StatementParsingTests.cs @@ -2705,6 +2705,23 @@ public void TestRunEmbeddedStatementNotFollowedBySemicolon() Assert.Equal((int)ErrorCode.ERR_SemicolonExpected, statement.Errors()[0].Code); } + [WorkItem(266237, "https://devdiv.visualstudio.com/DefaultCollection/DevDiv/_workitems?_a=edit&id=266237")] + [Fact] + public void NullExceptionInLabeledStatement() + { + UsingStatement(@"{ label: public", + // (1,1): error CS1073: Unexpected token 'public' + // { label: public + Diagnostic(ErrorCode.ERR_UnexpectedToken, "{ label: ").WithArguments("public").WithLocation(1, 1), + // (1,10): error CS1002: ; expected + // { label: public + Diagnostic(ErrorCode.ERR_SemicolonExpected, "public").WithLocation(1, 10), + // (1,10): error CS1513: } expected + // { label: public + Diagnostic(ErrorCode.ERR_RbraceExpected, "public").WithLocation(1, 10) + ); + } + private sealed class TokenAndTriviaWalker : CSharpSyntaxWalker { public int Tokens; From 6192c047bf645b4244849f9358dbfdf2a22f4c93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Fri, 5 May 2017 17:04:38 -0700 Subject: [PATCH 166/214] Remove DSRN VSIX prereq (#19304) --- src/VisualStudio/Setup/source.extension.vsixmanifest | 1 - 1 file changed, 1 deletion(-) diff --git a/src/VisualStudio/Setup/source.extension.vsixmanifest b/src/VisualStudio/Setup/source.extension.vsixmanifest index ddc6459c06620..98e129d348282 100644 --- a/src/VisualStudio/Setup/source.extension.vsixmanifest +++ b/src/VisualStudio/Setup/source.extension.vsixmanifest @@ -48,6 +48,5 @@ - From 6cfdbb3ce171c6ed5e501e17239250ae244ba437 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 5 May 2017 17:11:17 -0700 Subject: [PATCH 167/214] Add trivia to the statement. --- .../ArrowExpressionClauseSyntaxExtensions.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs index 1162a5fdf57c1..161837c0c8024 100644 --- a/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs +++ b/src/Workspaces/CSharp/Portable/Extensions/ArrowExpressionClauseSyntaxExtensions.cs @@ -23,15 +23,13 @@ public static bool TryConvertToBlock( return false; } - var openBrace = SyntaxFactory.Token(SyntaxKind.OpenBraceToken) - .WithTrailingTrivia(arrowExpression.ArrowToken.TrailingTrivia); - var statement = ConvertToStatement(arrowExpression.Expression, semicolonToken, createReturnStatementForExpression); + if (arrowExpression.ArrowToken.TrailingTrivia.Any(t => t.IsSingleOrMultiLineComment())) + { + statement = statement.WithPrependedLeadingTrivia(arrowExpression.ArrowToken.TrailingTrivia); + } - block = SyntaxFactory.Block( - openBrace, - SyntaxFactory.SingletonList(statement), - SyntaxFactory.Token(SyntaxKind.CloseBraceToken)); + block = SyntaxFactory.Block(statement); return true; } From 0f8b7ff4673e379708402364c67f12fbc99bc557 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 5 May 2017 19:37:21 -0700 Subject: [PATCH 168/214] Pool SHA1 instances. --- .../Workspace/Solution/Checksum_Factory.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 7a9e11b78ef01..28aa7ad967db6 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -14,14 +14,29 @@ namespace Microsoft.CodeAnalysis // all these are just helper methods internal partial class Checksum { + private static readonly Stack s_sha1Stack = new Stack(); + private static readonly object s_gate = new object(); + public static Checksum Create(Stream stream) { - // REVIEW: should we cache SHA1CryptoServiceProvider - using (var algorithm = SHA1.Create()) + SHA1 sha1; + lock (s_gate) { - stream.Seek(0, SeekOrigin.Begin); - return new Checksum(algorithm.ComputeHash(stream)); + sha1 = s_sha1Stack.Count > 0 + ? s_sha1Stack.Pop() + : SHA1.Create(); } + + stream.Seek(0, SeekOrigin.Begin); + + var checksum = new Checksum(sha1.ComputeHash(stream)); + + lock (s_gate) + { + s_sha1Stack.Push(sha1); + } + + return checksum; } public static Checksum Create(string kind, IObjectWritable @object) From 8fc3f6ee013a6a0b5d27b0b3f0c4fbbd1e184ffa Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Fri, 5 May 2017 20:38:46 -0700 Subject: [PATCH 169/214] Use a pooled array to create hashes from. --- .../Workspace/Solution/Checksum_Factory.cs | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 28aa7ad967db6..cf95cbab45a41 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -14,31 +14,53 @@ namespace Microsoft.CodeAnalysis // all these are just helper methods internal partial class Checksum { - private static readonly Stack s_sha1Stack = new Stack(); + private static readonly Stack s_hashStack = new Stack(); private static readonly object s_gate = new object(); public static Checksum Create(Stream stream) { - SHA1 sha1; + IncrementalHash hash; lock (s_gate) { - sha1 = s_sha1Stack.Count > 0 - ? s_sha1Stack.Pop() - : SHA1.Create(); + hash = s_hashStack.Count > 0 + ? s_hashStack.Pop() + : IncrementalHash.CreateHash(HashAlgorithmName.SHA1); } - stream.Seek(0, SeekOrigin.Begin); - - var checksum = new Checksum(sha1.ComputeHash(stream)); + var checksum = ComputeChecksum(stream, hash); lock (s_gate) { - s_sha1Stack.Push(sha1); + s_hashStack.Push(hash); } return checksum; } + private static Checksum ComputeChecksum(Stream stream, IncrementalHash hash) + { + using (var pooledBuffer = SharedPools.ByteArray.GetPooledObject()) + { + stream.Seek(0, SeekOrigin.Begin); + + var buffer = pooledBuffer.Object; + var bufferLength = buffer.Length; + int bytesRead; + do + { + bytesRead = stream.Read(buffer, 0, bufferLength); + if (bytesRead > 0) + { + hash.AppendData(buffer, 0, bytesRead); + } + } + while (bytesRead > 0); + + var bytes = hash.GetHashAndReset(); + return new Checksum(bytes); + } + } + public static Checksum Create(string kind, IObjectWritable @object) { using (var stream = SerializableBytes.CreateWritableStream()) From d66839db52902c8cfe3f45c5e47f6212b2cbb6c4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Sat, 6 May 2017 12:32:57 -0700 Subject: [PATCH 170/214] Pass LangVersion thru ICompilerOptionsHostObject.SetCompilerOptions when above 15 (VB) (#19112) --- src/Compilers/Core/MSBuildTask/Csc.cs | 6 - .../Core/MSBuildTask/ManagedCompiler.cs | 7 ++ src/Compilers/Core/MSBuildTask/Vbc.cs | 37 ++++-- .../VisualBasicCompilerOptionsTests.vb | 106 ++++++++++++++++++ .../ProjectSystemShim/VisualBasicProject.vb | 13 ++- .../VisualBasicProjectFileLoader.cs | 2 +- .../WorkspaceTests/MSBuildWorkspaceTests.cs | 21 ++++ ...sualBasicProject_VisualBasicProject.vbproj | 1 + 8 files changed, 178 insertions(+), 15 deletions(-) diff --git a/src/Compilers/Core/MSBuildTask/Csc.cs b/src/Compilers/Core/MSBuildTask/Csc.cs index 74bd2c7e5e360..edb5291946fa7 100644 --- a/src/Compilers/Core/MSBuildTask/Csc.cs +++ b/src/Compilers/Core/MSBuildTask/Csc.cs @@ -82,12 +82,6 @@ public bool GenerateFullPaths get { return _store.GetOrDefault(nameof(GenerateFullPaths), false); } } - public string LangVersion - { - set { _store[nameof(LangVersion)] = value; } - get { return (string)_store[nameof(LangVersion)]; } - } - public string ModuleAssemblyName { set { _store[nameof(ModuleAssemblyName)] = value; } diff --git a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs index b137cf9c561e6..c717c59741bfd 100644 --- a/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs +++ b/src/Compilers/Core/MSBuildTask/ManagedCompiler.cs @@ -382,6 +382,12 @@ protected override Encoding StandardOutputEncoding } } + public string LangVersion + { + set { _store[nameof(LangVersion)] = value; } + get { return (string)_store[nameof(LangVersion)]; } + } + #endregion /// @@ -733,6 +739,7 @@ internal void AddResponseFileCommandsForSwitchesSinceInitialReleaseThatAreNeeded commandLine.AppendSwitchIfNotNull("/checksumalgorithm:", ChecksumAlgorithm); commandLine.AppendSwitchWithSplitting("/instrument:", Instrument, ",", ';', ','); commandLine.AppendSwitchIfNotNull("/sourcelink:", SourceLink); + commandLine.AppendSwitchIfNotNull("/langversion:", LangVersion); AddFeatures(commandLine, Features); AddEmbeddedFilesToCommandLine(commandLine); diff --git a/src/Compilers/Core/MSBuildTask/Vbc.cs b/src/Compilers/Core/MSBuildTask/Vbc.cs index 8e56107794d13..355a8e6a07abb 100644 --- a/src/Compilers/Core/MSBuildTask/Vbc.cs +++ b/src/Compilers/Core/MSBuildTask/Vbc.cs @@ -79,12 +79,6 @@ public ITaskItem[] Imports get { return (ITaskItem[])_store[nameof(Imports)]; } } - public string LangVersion - { - set { _store[nameof(LangVersion)] = value; } - get { return (string)_store[nameof(LangVersion)]; } - } - public string ModuleAssemblyName { set { _store[nameof(ModuleAssemblyName)] = value; } @@ -909,7 +903,7 @@ private bool InitializeHostCompiler(IVbcHostObject vbcHostObject) } // Check for support of the LangVersion property - if (vbcHostObject is IVbcHostObject3) + if (vbcHostObject is IVbcHostObject3 && !DeferToICompilerOptionsHostObject(LangVersion, vbcHostObject)) { IVbcHostObject3 vbcHostObject3 = (IVbcHostObject3)vbcHostObject; CheckHostObjectSupport(param = nameof(LangVersion), vbcHostObject3.SetLanguageVersion(LangVersion)); @@ -960,6 +954,35 @@ private bool InitializeHostCompiler(IVbcHostObject vbcHostObject) return true; } + // VbcHostObject doesn't support VB versions beyond 15, + // so the LangVersion will be passed through ICompilerOptionsHostObject.SetCompilerOptions instead + private static bool DeferToICompilerOptionsHostObject(string langVersion, IVbcHostObject vbcHostObject) + { + if (!(vbcHostObject is ICompilerOptionsHostObject)) + { + return false; + } + + if (langVersion == null) + { + // CVbcMSBuildHostObject::SetLanguageVersion can handle null + return false; + } + + // CVbcMSBuildHostObject::SetLanguageVersion can handle versions up to 15 + var supportedList = new[] + { + "9", "9.0", + "10", "10.0", + "11", "11.0", + "12", "12.0", + "14", "14.0", + "15", "15.0" + }; + + return Array.IndexOf(supportedList, langVersion) < 0; + } + /// /// This method will get called during Execute() if a host object has been passed into the Vbc /// task. Returns one of the following values to indicate what the next action should be: diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualBasicCompilerOptionsTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualBasicCompilerOptionsTests.vb index 1b82262cbde41..f55ff538c3e19 100644 --- a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualBasicCompilerOptionsTests.vb +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualBasicCompilerOptionsTests.vb @@ -47,6 +47,112 @@ Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim End Using End Sub + + + Public Sub SetCompilerOptions_LangVersion14() + Using environment = New TestEnvironment() + Dim project = CreateVisualBasicProject(environment, "Test") + + Dim compilerOptionsHost = DirectCast(project, Implementation.ProjectSystem.Interop.ICompilerOptionsHostObject) + Dim supported As Boolean + compilerOptionsHost.SetCompilerOptions("/langversion:14", supported) + Assert.True(supported) + + Dim workspaceProject = environment.Workspace.CurrentSolution.Projects.Single() + Dim options = DirectCast(workspaceProject.ParseOptions, VisualBasicParseOptions) + + ' SetCompilerOptions only handles versions 15.3 and up + Assert.Equal(LanguageVersion.VisualBasic15, options.LanguageVersion) + Assert.Equal(LanguageVersion.VisualBasic15, options.SpecifiedLanguageVersion) + + project.Disconnect() + End Using + End Sub + + + + Public Sub SetCompilerOptions_LangVersion15() + Using environment = New TestEnvironment() + Dim project = CreateVisualBasicProject(environment, "Test") + + Dim compilerOptionsHost = DirectCast(project, Implementation.ProjectSystem.Interop.ICompilerOptionsHostObject) + Dim supported As Boolean + compilerOptionsHost.SetCompilerOptions("/langversion:15", supported) + Assert.True(supported) + + Dim workspaceProject = environment.Workspace.CurrentSolution.Projects.Single() + Dim options = DirectCast(workspaceProject.ParseOptions, VisualBasicParseOptions) + + Assert.Equal(LanguageVersion.VisualBasic15, options.LanguageVersion) + Assert.Equal(LanguageVersion.VisualBasic15, options.SpecifiedLanguageVersion) + + project.Disconnect() + End Using + End Sub + + + + Public Sub SetCompilerOptions_LangVersionDefault() + Using environment = New TestEnvironment() + Dim project = CreateVisualBasicProject(environment, "Test") + + Dim compilerOptionsHost = DirectCast(project, Implementation.ProjectSystem.Interop.ICompilerOptionsHostObject) + Dim supported As Boolean + compilerOptionsHost.SetCompilerOptions("/langversion:Default", supported) + Assert.True(supported) + + Dim workspaceProject = environment.Workspace.CurrentSolution.Projects.Single() + Dim options = DirectCast(workspaceProject.ParseOptions, VisualBasicParseOptions) + + Assert.Equal(LanguageVersion.Default.MapSpecifiedToEffectiveVersion(), options.LanguageVersion) + Assert.Equal(LanguageVersion.Default.MapSpecifiedToEffectiveVersion(), options.SpecifiedLanguageVersion) + + project.Disconnect() + End Using + End Sub + + + + Public Sub SetCompilerOptions_LangVersion15_3() + Using environment = New TestEnvironment() + Dim project = CreateVisualBasicProject(environment, "Test") + + Dim compilerOptionsHost = DirectCast(project, Implementation.ProjectSystem.Interop.ICompilerOptionsHostObject) + Dim supported As Boolean + compilerOptionsHost.SetCompilerOptions("/langversion:15.3", supported) + Assert.True(supported) + + Dim workspaceProject = environment.Workspace.CurrentSolution.Projects.Single() + Dim options = DirectCast(workspaceProject.ParseOptions, VisualBasicParseOptions) + + Assert.Equal(LanguageVersion.VisualBasic15_3, options.LanguageVersion) + Assert.Equal(LanguageVersion.VisualBasic15_3, options.SpecifiedLanguageVersion) + + project.Disconnect() + End Using + End Sub + + + + Public Sub SetCompilerOptions_LangVersionLatest() + Using environment = New TestEnvironment() + Dim project = CreateVisualBasicProject(environment, "Test") + + Dim compilerOptionsHost = DirectCast(project, Implementation.ProjectSystem.Interop.ICompilerOptionsHostObject) + Dim supported As Boolean + compilerOptionsHost.SetCompilerOptions("/langversion:latest", supported) + Assert.True(supported) + + Dim workspaceProject = environment.Workspace.CurrentSolution.Projects.Single() + Dim options = DirectCast(workspaceProject.ParseOptions, VisualBasicParseOptions) + + Assert.Equal(LanguageVersion.Latest.MapSpecifiedToEffectiveVersion(), options.LanguageVersion) + Assert.Equal(LanguageVersion.Latest.MapSpecifiedToEffectiveVersion(), options.SpecifiedLanguageVersion) + + project.Disconnect() + End Using + End Sub + diff --git a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb index ac6fd0531302c..ac629cfba7be5 100644 --- a/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb +++ b/src/VisualStudio/VisualBasic/Impl/ProjectSystemShim/VisualBasicProject.vb @@ -392,7 +392,18 @@ Namespace Microsoft.VisualStudio.LanguageServices.VisualBasic.ProjectSystemShim Protected Overrides Function CreateParseOptions(commandLineArguments As CommandLineArguments) As ParseOptions Dim baseParseOptions = DirectCast(MyBase.CreateParseOptions(commandLineArguments), VisualBasicParseOptions) - Return VisualBasicProjectOptionsHelper.CreateParseOptions(baseParseOptions, _rawOptions) + + Dim resultParseOptions = VisualBasicProjectOptionsHelper.CreateParseOptions(baseParseOptions, _rawOptions) + + Dim commandLineOptions = DirectCast(commandLineArguments.ParseOptions, VisualBasicParseOptions) + If commandLineOptions.LanguageVersion > LanguageVersion.VisualBasic15 Then + ' For language versions after VB 15, we expect the version to be passed from MSBuild to the IDE + ' via command-line arguments (`ICompilerOptionsHostObject.SetCompilerOptions`) + ' instead of using `IVbcHostObject3.SetLanguageVersion` + resultParseOptions = resultParseOptions.WithLanguageVersion(commandLineOptions.LanguageVersion) + End If + + Return resultParseOptions End Function Private Shadows Sub UpdateOptions() diff --git a/src/Workspaces/Core/Desktop/Workspace/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs b/src/Workspaces/Core/Desktop/Workspace/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs index b8e597b49880b..417c6b2eb6a1d 100644 --- a/src/Workspaces/Core/Desktop/Workspace/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs +++ b/src/Workspaces/Core/Desktop/Workspace/MSBuild/VisualBasic/VisualBasicProjectFileLoader.cs @@ -880,7 +880,7 @@ public bool SetLanguageVersion(string languageVersion) { if (!string.IsNullOrWhiteSpace(languageVersion)) { - _commandLineArgs.Add("/languageversion:" + languageVersion); + _commandLineArgs.Add("/langversion:" + languageVersion); } return true; diff --git a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs index cef5a03d23ca3..7b32f25925d16 100644 --- a/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs +++ b/src/Workspaces/CoreTest/WorkspaceTests/MSBuildWorkspaceTests.cs @@ -580,6 +580,27 @@ public void TestOpenProject_VisualBasic_WithoutOutputPath() Assert.NotEmpty(project.OutputFilePath); } + [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] + public void TestOpenProject_VisualBasic_WithLanguageVersion15_3() + { + CreateFiles(GetMultiProjectSolutionFiles() + .ReplaceFileElement(@"VisualBasicProject\VisualBasicProject.vbproj", "LangVersion", "15.3")); + + var project = MSBuildWorkspace.Create().OpenProjectAsync(GetSolutionFileName(@"VisualBasicProject\VisualBasicProject.vbproj")).Result; + Assert.Equal(VB.LanguageVersion.VisualBasic15_3, ((VB.VisualBasicParseOptions)project.ParseOptions).LanguageVersion); + } + + [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] + public void TestOpenProject_VisualBasic_WithLatestLanguageVersion() + { + CreateFiles(GetMultiProjectSolutionFiles() + .ReplaceFileElement(@"VisualBasicProject\VisualBasicProject.vbproj", "LangVersion", "Latest")); + + var project = MSBuildWorkspace.Create().OpenProjectAsync(GetSolutionFileName(@"VisualBasicProject\VisualBasicProject.vbproj")).Result; + Assert.Equal(VB.LanguageVersion.VisualBasic15_3, ((VB.VisualBasicParseOptions)project.ParseOptions).LanguageVersion); + Assert.Equal(VB.LanguageVersion.Latest, ((VB.VisualBasicParseOptions)project.ParseOptions).SpecifiedLanguageVersion); + } + [Fact, Trait(Traits.Feature, Traits.Features.Workspace)] public void TestOpenProject_VisualBasic_WithoutAssemblyName() { diff --git a/src/Workspaces/CoreTestUtilities/TestFiles/VisualBasicProject_VisualBasicProject.vbproj b/src/Workspaces/CoreTestUtilities/TestFiles/VisualBasicProject_VisualBasicProject.vbproj index 4485d47d75b54..53e7391baea7a 100644 --- a/src/Workspaces/CoreTestUtilities/TestFiles/VisualBasicProject_VisualBasicProject.vbproj +++ b/src/Workspaces/CoreTestUtilities/TestFiles/VisualBasicProject_VisualBasicProject.vbproj @@ -38,6 +38,7 @@ On + 15 Binary From f6df6d59cf2f6c7414e7f9612dcdfb4286339805 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 14:55:48 -0700 Subject: [PATCH 171/214] Improve performance of tree serialization. --- .../ObjectSerializationTests.cs | 3 +- .../Portable/Serialization/ObjectWriter.cs | 37 +++++++++++++++---- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs index d01a7f9d25ce9..48e91cafcf691 100644 --- a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs @@ -919,7 +919,8 @@ private static void TestWritingPrimitiveValues(ObjectWriter writer) writer.WriteValue("\uD800\uDC00"); // valid surrogate pair writer.WriteValue("\uDC00\uD800"); // invalid surrogate pair writer.WriteValue("\uD800"); // incomplete surrogate pair - writer.WriteValue(null); + writer.WriteValue((object)null); + writer.WriteValue((IObjectWritable)null); unchecked { writer.WriteInt64((long)ConsoleColor.Cyan); diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index eb80d068444a2..f3bbdb87fad4e 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -215,10 +215,21 @@ public void WriteValue(object value) } else { - WriteObject(value); + WriteObject(instance: value, instanceAsWritableOpt: null); } } + public void WriteValue(IObjectWritable value) + { + if (value == null) + { + _writer.Write((byte)EncodingKind.Null); + return; + } + + WriteObject(instance: value, instanceAsWritableOpt: value); + } + private void WriteEncodedInt32(int v) { if (v >= 0 && v <= 10) @@ -670,13 +681,15 @@ private void WriteKnownType(Type type) this.WriteInt32(_binderSnapshot.GetTypeId(type)); } - private void WriteObject(object instance) + private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) { + Debug.Assert(instance != null); + Debug.Assert(instanceAsWritableOpt == null || instance == instanceAsWritableOpt); + _cancellationToken.ThrowIfCancellationRequested(); // write object ref if we already know this instance - int id; - if (_objectReferenceMap.TryGetReferenceId(instance, out id)) + if (_objectReferenceMap.TryGetReferenceId(instance, out var id)) { Debug.Assert(id >= 0); if (id <= byte.MaxValue) @@ -697,10 +710,18 @@ private void WriteObject(object instance) } else { - var writable = instance as IObjectWritable; - if (writable == null) + IObjectWritable writable; + if (instanceAsWritableOpt != null) { - throw NoSerializationWriterException($"{instance.GetType()} must implement {nameof(IObjectWritable)}"); + writable = instanceAsWritableOpt; + } + else + { + writable = instance as IObjectWritable; + if (writable == null) + { + throw NoSerializationWriterException($"{instance.GetType()} must implement {nameof(IObjectWritable)}"); + } } var oldDepth = _recursionDepth; @@ -712,7 +733,7 @@ private void WriteObject(object instance) // don't blow the stack. 'LongRunning' ensures that we get a dedicated thread // to do this work. That way we don't end up blocking the threadpool. var task = Task.Factory.StartNew( - () => WriteObjectWorker(instance, writable), + () => WriteObjectWorker(instance, writable), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); From 0af082f87aec0a39b56a124279ff37508c0e4172 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 14:58:32 -0700 Subject: [PATCH 172/214] Simplify. --- .../Core/Portable/Serialization/ObjectWriter.cs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index f3bbdb87fad4e..9850d9c4f1b11 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -710,15 +710,10 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } else { - IObjectWritable writable; - if (instanceAsWritableOpt != null) + if (instanceAsWritableOpt == null) { - writable = instanceAsWritableOpt; - } - else - { - writable = instance as IObjectWritable; - if (writable == null) + instanceAsWritableOpt = instance as IObjectWritable; + if (instanceAsWritableOpt == null) { throw NoSerializationWriterException($"{instance.GetType()} must implement {nameof(IObjectWritable)}"); } @@ -733,7 +728,7 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) // don't blow the stack. 'LongRunning' ensures that we get a dedicated thread // to do this work. That way we don't end up blocking the threadpool. var task = Task.Factory.StartNew( - () => WriteObjectWorker(instance, writable), + () => WriteObjectWorker(instance, instanceAsWritableOpt), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); @@ -741,7 +736,7 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } else { - WriteObjectWorker(instance, writable); + WriteObjectWorker(instance, instanceAsWritableOpt); } _recursionDepth--; From 89b38d501440ca416dee1cbd9eac23972c302723 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 15:01:43 -0700 Subject: [PATCH 173/214] Inline decl. --- src/Compilers/Core/Portable/Serialization/ObjectWriter.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index 9850d9c4f1b11..d2acf9c219c38 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -449,8 +449,7 @@ private void WriteArray(Array array) var elementType = array.GetType().GetElementType(); - EncodingKind elementKind; - if (s_typeMap.TryGetValue(elementType, out elementKind)) + if (s_typeMap.TryGetValue(elementType, out var elementKind)) { this.WritePrimitiveType(elementType, elementKind); this.WritePrimitiveTypeArrayElements(elementType, elementKind, array); @@ -470,7 +469,7 @@ private void WriteArray(Array array) // don't blow the stack. 'LongRunning' ensures that we get a dedicated thread // to do this work. That way we don't end up blocking the threadpool. var task = Task.Factory.StartNew( - () => WriteArrayValues(array), + () => WriteArrayValues(array), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); From 2a736626f986bc55b432a6fc3e19d9a75c71d0bd Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 15:03:35 -0700 Subject: [PATCH 174/214] Simplify. --- .../Core/Portable/Serialization/ObjectWriter.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs index d2acf9c219c38..2e4a6b8f8508b 100644 --- a/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs +++ b/src/Compilers/Core/Portable/Serialization/ObjectWriter.cs @@ -709,10 +709,11 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } else { - if (instanceAsWritableOpt == null) + var writable = instanceAsWritableOpt; + if (writable == null) { - instanceAsWritableOpt = instance as IObjectWritable; - if (instanceAsWritableOpt == null) + writable = instance as IObjectWritable; + if (writable == null) { throw NoSerializationWriterException($"{instance.GetType()} must implement {nameof(IObjectWritable)}"); } @@ -727,7 +728,7 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) // don't blow the stack. 'LongRunning' ensures that we get a dedicated thread // to do this work. That way we don't end up blocking the threadpool. var task = Task.Factory.StartNew( - () => WriteObjectWorker(instance, instanceAsWritableOpt), + () => WriteObjectWorker(instance, writable), _cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Default); @@ -735,7 +736,7 @@ private void WriteObject(object instance, IObjectWritable instanceAsWritableOpt) } else { - WriteObjectWorker(instance, instanceAsWritableOpt); + WriteObjectWorker(instance, writable); } _recursionDepth--; From a91039c1e65a545bdca863e26038584fdd450e0b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 16:09:40 -0700 Subject: [PATCH 175/214] Don't use a string-kind for checksums. --- .../Core/Next/Remote/JsonRpcSession.cs | 4 +- .../Core/Portable/Execution/CustomAsset.cs | 8 +- .../Core/Portable/Execution/Extensions.cs | 122 ++++-------------- .../Core/Portable/Execution/RemotableData.cs | 4 +- .../Core/Portable/Execution/Serializer.cs | 8 +- .../Serializer_ChecksumWithChildren.cs | 10 +- .../Core/Portable/Execution/SolutionAsset.cs | 3 +- .../WellKnownSynchronizationKinds.cs | 71 ++++++---- .../SymbolTree/SymbolTreeInfo_Source.cs | 2 +- .../SyntaxTree/SyntaxTreeIndex_Persistence.cs | 2 +- .../Workspace/Solution/ChecksumCollection.cs | 16 +-- .../Solution/ChecksumWithChildren.cs | 4 +- .../Workspace/Solution/Checksum_Factory.cs | 16 +-- .../Workspace/Solution/DocumentInfo.cs | 3 +- .../Workspace/Solution/ProjectInfo.cs | 3 +- .../Workspace/Solution/SolutionInfo.cs | 3 +- .../Workspace/Solution/StateChecksums.cs | 6 +- .../SnapshotSerializationTestBase.cs | 10 +- .../Remote/Core/Services/AssetService.cs | 2 +- .../SnapshotService.JsonRpcAssetSource.cs | 3 +- 20 files changed, 122 insertions(+), 178 deletions(-) diff --git a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs index 95318f119232e..4cb8181546bca 100644 --- a/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs +++ b/src/VisualStudio/Core/Next/Remote/JsonRpcSession.cs @@ -236,7 +236,7 @@ private async Task WriteOneAssetAsync(ObjectWriter writer, Checksum checksum) writer.WriteInt32(1); checksum.WriteTo(writer); - writer.WriteString(remotableData.Kind); + writer.WriteInt32((int)remotableData.Kind); await remotableData.WriteObjectToAsync(writer, _source.Token).ConfigureAwait(false); } @@ -252,7 +252,7 @@ private async Task WriteMultipleAssetsAsync(ObjectWriter writer, Checksum[] chec var remotableData = kv.Value; checksum.WriteTo(writer); - writer.WriteString(remotableData.Kind); + writer.WriteInt32((int)remotableData.Kind); await remotableData.WriteObjectToAsync(writer, _source.Token).ConfigureAwait(false); } diff --git a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs index 59bd6c8cb2824..3d9058d48f04c 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Execution /// internal abstract class CustomAsset : RemotableData { - public CustomAsset(Checksum checksum, string kind) : base(checksum, kind) + public CustomAsset(Checksum checksum, WellKnownSynchronizationKinds kind) : base(checksum, kind) { } } @@ -26,7 +26,7 @@ internal sealed class SimpleCustomAsset : CustomAsset { private readonly Action _writer; - public SimpleCustomAsset(string kind, Action writer) : + public SimpleCustomAsset(WellKnownSynchronizationKinds kind, Action writer) : base(CreateChecksumFromStreamWriter(kind, writer), kind) { // unlike SolutionAsset which gets checksum from solution states, this one build one by itself. @@ -39,12 +39,12 @@ public override Task WriteObjectToAsync(ObjectWriter writer, CancellationToken c return SpecializedTasks.EmptyTask; } - private static Checksum CreateChecksumFromStreamWriter(string kind, Action writer) + private static Checksum CreateChecksumFromStreamWriter(WellKnownSynchronizationKinds kind, Action writer) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) { - objectWriter.WriteString(kind); + objectWriter.WriteInt32((int)kind); writer(objectWriter, CancellationToken.None); return Checksum.Create(stream); } diff --git a/src/Workspaces/Core/Portable/Execution/Extensions.cs b/src/Workspaces/Core/Portable/Execution/Extensions.cs index 12aced78a25a0..481b1d600c9c4 100644 --- a/src/Workspaces/Core/Portable/Execution/Extensions.cs +++ b/src/Workspaces/Core/Portable/Execution/Extensions.cs @@ -15,106 +15,30 @@ public static T[] ReadArray(this ObjectReader reader) return (T[])reader.ReadValue(); } - public static string GetWellKnownSynchronizationKind(this object value) + public static WellKnownSynchronizationKinds GetWellKnownSynchronizationKind(this object value) { - if (value is SolutionStateChecksums) - { - return WellKnownSynchronizationKinds.SolutionState; - } - - if (value is ProjectStateChecksums) - { - return WellKnownSynchronizationKinds.ProjectState; - } - - if (value is DocumentStateChecksums) - { - return WellKnownSynchronizationKinds.DocumentState; - } - - if (value is ProjectChecksumCollection) - { - return WellKnownSynchronizationKinds.Projects; - } - - if (value is DocumentChecksumCollection) - { - return WellKnownSynchronizationKinds.Documents; - } - - if (value is TextDocumentChecksumCollection) - { - return WellKnownSynchronizationKinds.TextDocuments; - } - - if (value is ProjectReferenceChecksumCollection) - { - return WellKnownSynchronizationKinds.ProjectReferences; - } - - if (value is MetadataReferenceChecksumCollection) - { - return WellKnownSynchronizationKinds.MetadataReferences; - } - - if (value is AnalyzerReferenceChecksumCollection) - { - return WellKnownSynchronizationKinds.AnalyzerReferences; - } - - if (value is SolutionInfo.SolutionAttributes) - { - return WellKnownSynchronizationKinds.SolutionAttributes; - } - - if (value is ProjectInfo.ProjectAttributes) - { - return WellKnownSynchronizationKinds.ProjectAttributes; - } - - if (value is DocumentInfo.DocumentAttributes) - { - return WellKnownSynchronizationKinds.DocumentAttributes; - } - - if (value is CompilationOptions) - { - return WellKnownSynchronizationKinds.CompilationOptions; - } - - if (value is ParseOptions) - { - return WellKnownSynchronizationKinds.ParseOptions; - } - - if (value is ProjectReference) - { - return WellKnownSynchronizationKinds.ProjectReference; - } - - if (value is MetadataReference) - { - return WellKnownSynchronizationKinds.MetadataReference; - } - - if (value is AnalyzerReference) - { - return WellKnownSynchronizationKinds.AnalyzerReference; - } - - if (value is TextDocumentState) - { - return WellKnownSynchronizationKinds.RecoverableSourceText; - } - - if (value is SourceText) - { - return WellKnownSynchronizationKinds.SourceText; - } - - if (value is OptionSet) - { - return WellKnownSynchronizationKinds.OptionSet; + switch (value) + { + case SolutionStateChecksums _: return WellKnownSynchronizationKinds.SolutionState; + case ProjectStateChecksums _: return WellKnownSynchronizationKinds.ProjectState; + case DocumentStateChecksums _: return WellKnownSynchronizationKinds.DocumentState; + case ProjectChecksumCollection _: return WellKnownSynchronizationKinds.Projects; + case DocumentChecksumCollection _: return WellKnownSynchronizationKinds.Documents; + case TextDocumentChecksumCollection _: return WellKnownSynchronizationKinds.TextDocuments; + case ProjectReferenceChecksumCollection _: return WellKnownSynchronizationKinds.ProjectReferences; + case MetadataReferenceChecksumCollection _: return WellKnownSynchronizationKinds.MetadataReferences; + case AnalyzerReferenceChecksumCollection _: return WellKnownSynchronizationKinds.AnalyzerReferences; + case SolutionInfo.SolutionAttributes _: return WellKnownSynchronizationKinds.SolutionAttributes; + case ProjectInfo.ProjectAttributes _: return WellKnownSynchronizationKinds.ProjectAttributes; + case DocumentInfo.DocumentAttributes _: return WellKnownSynchronizationKinds.DocumentAttributes; + case CompilationOptions _: return WellKnownSynchronizationKinds.CompilationOptions; + case ParseOptions _: return WellKnownSynchronizationKinds.ParseOptions; + case ProjectReference _: return WellKnownSynchronizationKinds.ProjectReference; + case MetadataReference _: return WellKnownSynchronizationKinds.MetadataReference; + case AnalyzerReference _: return WellKnownSynchronizationKinds.AnalyzerReference; + case TextDocumentState _: return WellKnownSynchronizationKinds.RecoverableSourceText; + case SourceText _: return WellKnownSynchronizationKinds.SourceText; + case OptionSet _: return WellKnownSynchronizationKinds.OptionSet; } throw ExceptionUtilities.UnexpectedValue(value); diff --git a/src/Workspaces/Core/Portable/Execution/RemotableData.cs b/src/Workspaces/Core/Portable/Execution/RemotableData.cs index 1dd7218ea3dfc..94fa61fffc84e 100644 --- a/src/Workspaces/Core/Portable/Execution/RemotableData.cs +++ b/src/Workspaces/Core/Portable/Execution/RemotableData.cs @@ -21,14 +21,14 @@ internal abstract partial class RemotableData /// this will be used in tranportation framework and deserialization service /// to hand shake how to send over data and deserialize serialized data /// - public readonly string Kind; + public readonly WellKnownSynchronizationKinds Kind; /// /// Checksum of this object /// public readonly Checksum Checksum; - public RemotableData(Checksum checksum, string kind) + public RemotableData(Checksum checksum, WellKnownSynchronizationKinds kind) { Checksum = checksum; Kind = kind; diff --git a/src/Workspaces/Core/Portable/Execution/Serializer.cs b/src/Workspaces/Core/Portable/Execution/Serializer.cs index d7d69d89c5bc7..e5b0e4ad90fb4 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer.cs @@ -52,7 +52,7 @@ public Checksum CreateChecksum(object value, CancellationToken cancellationToken { var kind = value.GetWellKnownSynchronizationKind(); - using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, kind, cancellationToken)) + using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); @@ -92,7 +92,7 @@ public void Serialize(object value, ObjectWriter writer, CancellationToken cance { var kind = value.GetWellKnownSynchronizationKind(); - using (Logger.LogBlock(FunctionId.Serializer_Serialize, kind, cancellationToken)) + using (Logger.LogBlock(FunctionId.Serializer_Serialize, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); @@ -146,9 +146,9 @@ public void Serialize(object value, ObjectWriter writer, CancellationToken cance } } - public T Deserialize(string kind, ObjectReader reader, CancellationToken cancellationToken) + public T Deserialize(WellKnownSynchronizationKinds kind, ObjectReader reader, CancellationToken cancellationToken) { - using (Logger.LogBlock(FunctionId.Serializer_Deserialize, kind, cancellationToken)) + using (Logger.LogBlock(FunctionId.Serializer_Deserialize, kind.ToString(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs b/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs index aa977b72fc1f9..38f4aafe4dd4b 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs @@ -17,14 +17,14 @@ internal partial class Serializer private const byte ChecksumKind = 0; private const byte ChecksumWithChildrenKind = 1; - private static readonly ImmutableDictionary> s_creatorMap = CreateCreatorMap(); + private static readonly ImmutableDictionary> s_creatorMap = CreateCreatorMap(); public void SerializeChecksumWithChildren(ChecksumWithChildren checksums, ObjectWriter writer, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); var kind = checksums.GetWellKnownSynchronizationKind(); - writer.WriteString(kind); + writer.WriteInt32((int)kind); checksums.Checksum.WriteTo(writer); writer.WriteInt32(checksums.Children.Count); @@ -54,7 +54,7 @@ private ChecksumWithChildren DeserializeChecksumWithChildren(ObjectReader reader { cancellationToken.ThrowIfCancellationRequested(); - var kind = reader.ReadString(); + var kind = (WellKnownSynchronizationKinds)reader.ReadInt32(); var checksum = Checksum.ReadFrom(reader); var childrenCount = reader.ReadInt32(); @@ -84,9 +84,9 @@ private ChecksumWithChildren DeserializeChecksumWithChildren(ObjectReader reader return checksums; } - private static ImmutableDictionary> CreateCreatorMap() + private static ImmutableDictionary> CreateCreatorMap() { - return ImmutableDictionary>.Empty + return ImmutableDictionary>.Empty .Add(WellKnownSynchronizationKinds.SolutionState, children => new SolutionStateChecksums(children)) .Add(WellKnownSynchronizationKinds.ProjectState, children => new ProjectStateChecksums(children)) .Add(WellKnownSynchronizationKinds.DocumentState, children => new DocumentStateChecksums(children)) diff --git a/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs b/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs index 067df7d7a18b9..124bc1cf7af86 100644 --- a/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs @@ -13,7 +13,8 @@ namespace Microsoft.CodeAnalysis.Execution /// internal abstract class SolutionAsset : RemotableData { - protected SolutionAsset(Checksum checksum, string kind) : base(checksum, kind) + protected SolutionAsset(Checksum checksum, WellKnownSynchronizationKinds kind) + : base(checksum, kind) { } diff --git a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs index c81a8793f089f..066d0b55ca22a 100644 --- a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs +++ b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs @@ -3,33 +3,48 @@ namespace Microsoft.CodeAnalysis.Serialization { // TODO: Kind might not actually needed. see whether we can get rid of this - internal static class WellKnownSynchronizationKinds + internal enum WellKnownSynchronizationKinds { - public const string Null = nameof(Null); - - public const string SolutionState = nameof(SolutionStateChecksums); - public const string ProjectState = nameof(ProjectStateChecksums); - public const string DocumentState = nameof(DocumentStateChecksums); - - public const string Projects = nameof(ProjectChecksumCollection); - public const string Documents = nameof(DocumentChecksumCollection); - public const string TextDocuments = nameof(TextDocumentChecksumCollection); - public const string ProjectReferences = nameof(ProjectReferenceChecksumCollection); - public const string MetadataReferences = nameof(MetadataReferenceChecksumCollection); - public const string AnalyzerReferences = nameof(AnalyzerReferenceChecksumCollection); - - public const string SolutionAttributes = nameof(SolutionInfo.SolutionAttributes); - public const string ProjectAttributes = nameof(ProjectInfo.ProjectAttributes); - public const string DocumentAttributes = nameof(DocumentInfo.DocumentAttributes); - - public const string CompilationOptions = nameof(CompilationOptions); - public const string ParseOptions = nameof(ParseOptions); - public const string ProjectReference = nameof(ProjectReference); - public const string MetadataReference = nameof(MetadataReference); - public const string AnalyzerReference = nameof(AnalyzerReference); - public const string SourceText = nameof(SourceText); - public const string OptionSet = nameof(OptionSet); - - public const string RecoverableSourceText = nameof(RecoverableSourceText); + Null, + + SolutionState, + ProjectState, + DocumentState, + + Projects, + Documents, + TextDocuments, + ProjectReferences, + MetadataReferences, + AnalyzerReferences, + + SolutionAttributes, + ProjectAttributes, + DocumentAttributes, + + CompilationOptions, + ParseOptions, + ProjectReference, + MetadataReference, + AnalyzerReference, + SourceText, + OptionSet, + + RecoverableSourceText, + + // + + SyntaxTreeIndex, + SymbolTreeInfo, + + ProjectReferenceChecksumCollection, + MetadataReferenceChecksumCollection, + AnalyzerReferenceChecksumCollection, + TextDocumentChecksumCollection, + DocumentChecksumCollection, + ProjectChecksumCollection, + SolutionStateChecksums, + ProjectStateChecksums, + DocumentStateChecksums, } -} +} \ No newline at end of file diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 3993bef464d00..a5d80a8a135a5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -66,7 +66,7 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project allChecksums.Add(compilationOptionsChecksum); allChecksums.Add(parseOptionsChecksum); - var checksum = Checksum.Create(nameof(SymbolTreeInfo), allChecksums); + var checksum = Checksum.Create(WellKnownSynchronizationKinds.SymbolTreeInfo, allChecksums); return checksum; } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index 3c99216793b57..a0a6a0d585901 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -89,7 +89,7 @@ public static async Task GetChecksumAsync( var parseOptionsChecksum = ChecksumCache.GetOrCreate( parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); - return Checksum.Create(nameof(SyntaxTreeIndex), new[] { textChecksum, parseOptionsChecksum }); + return Checksum.Create(WellKnownSynchronizationKinds.SyntaxTreeIndex, new[] { textChecksum, parseOptionsChecksum }); } private async Task SaveAsync( diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs index 7e1d0970ebaf7..1d5a72da8fab4 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs @@ -12,11 +12,11 @@ namespace Microsoft.CodeAnalysis.Serialization /// internal abstract class ChecksumCollection : ChecksumWithChildren, IEnumerable { - protected ChecksumCollection(string kind, Checksum[] checksums) : this(kind, (object[])checksums) + protected ChecksumCollection(WellKnownSynchronizationKinds kind, Checksum[] checksums) : this(kind, (object[])checksums) { } - protected ChecksumCollection(string kind, object[] checksums) : base(kind, checksums) + protected ChecksumCollection(WellKnownSynchronizationKinds kind, object[] checksums) : base(kind, checksums) { } @@ -38,36 +38,36 @@ IEnumerator IEnumerable.GetEnumerator() internal class ProjectChecksumCollection : ChecksumCollection { public ProjectChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public ProjectChecksumCollection(object[] checksums) : base(nameof(ProjectChecksumCollection), checksums) { } + public ProjectChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.ProjectChecksumCollection, checksums) { } } internal class DocumentChecksumCollection : ChecksumCollection { public DocumentChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public DocumentChecksumCollection(object[] checksums) : base(nameof(DocumentChecksumCollection), checksums) { } + public DocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.DocumentChecksumCollection, checksums) { } } internal class TextDocumentChecksumCollection : ChecksumCollection { public TextDocumentChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public TextDocumentChecksumCollection(object[] checksums) : base(nameof(TextDocumentChecksumCollection), checksums) { } + public TextDocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.TextDocumentChecksumCollection, checksums) { } } internal class ProjectReferenceChecksumCollection : ChecksumCollection { public ProjectReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public ProjectReferenceChecksumCollection(object[] checksums) : base(nameof(ProjectReferenceChecksumCollection), checksums) { } + public ProjectReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.ProjectReferenceChecksumCollection, checksums) { } } internal class MetadataReferenceChecksumCollection : ChecksumCollection { public MetadataReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public MetadataReferenceChecksumCollection(object[] checksums) : base(nameof(MetadataReferenceChecksumCollection), checksums) { } + public MetadataReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.MetadataReferenceChecksumCollection, checksums) { } } internal class AnalyzerReferenceChecksumCollection : ChecksumCollection { public AnalyzerReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public AnalyzerReferenceChecksumCollection(object[] checksums) : base(nameof(AnalyzerReferenceChecksumCollection), checksums) { } + public AnalyzerReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.AnalyzerReferenceChecksumCollection, checksums) { } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs index d40ba8c5073dd..43461c80b0d76 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Serialization /// internal abstract class ChecksumWithChildren : IChecksummedObject { - public ChecksumWithChildren(string kind, params object[] children) + public ChecksumWithChildren(WellKnownSynchronizationKinds kind, params object[] children) { Checksum = CreateChecksum(kind, children); Children = children; @@ -20,7 +20,7 @@ public ChecksumWithChildren(string kind, params object[] children) public IReadOnlyList Children { get; } - private static Checksum CreateChecksum(string kind, object[] children) + private static Checksum CreateChecksum(WellKnownSynchronizationKinds kind, object[] children) { // given children must be either Checksum or Checksums (collection of a checksum) return Checksum.Create(kind, children.Select(c => c as Checksum ?? ((ChecksumCollection)c).Checksum)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index 7a9e11b78ef01..dbc50f77793f8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -24,24 +24,24 @@ public static Checksum Create(Stream stream) } } - public static Checksum Create(string kind, IObjectWritable @object) + public static Checksum Create(WellKnownSynchronizationKinds kind, IObjectWritable @object) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) { - objectWriter.WriteString(kind); + objectWriter.WriteInt32((int)kind); @object.WriteTo(objectWriter); return Create(stream); } } - public static Checksum Create(string kind, IEnumerable checksums) + public static Checksum Create(WellKnownSynchronizationKinds kind, IEnumerable checksums) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream)) { - writer.WriteString(kind); + writer.WriteInt32((int)kind); foreach (var checksum in checksums) { @@ -52,12 +52,12 @@ public static Checksum Create(string kind, IEnumerable checksums) } } - public static Checksum Create(string kind, ImmutableArray bytes) + public static Checksum Create(WellKnownSynchronizationKinds kind, ImmutableArray bytes) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream)) { - writer.WriteString(kind); + writer.WriteInt32((int)kind); for (var i = 0; i < bytes.Length; i++) { @@ -68,12 +68,12 @@ public static Checksum Create(string kind, ImmutableArray bytes) } } - public static Checksum Create(string kind, T value, Serializer serializer) + public static Checksum Create(WellKnownSynchronizationKinds kind, T value, Serializer serializer) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) { - objectWriter.WriteString(kind); + objectWriter.WriteInt32((int)kind); serializer.Serialize(value, objectWriter, CancellationToken.None); return Create(stream); } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index e675cad81bec5..0ea5dc98aa411 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -234,7 +235,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(nameof(DocumentAttributes), this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.DocumentAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs index 04d936c2a10e8..b0f34d640040e 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -474,7 +475,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(nameof(ProjectAttributes), this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.ProjectAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs index f120edfdc0e83..f5d9029c9ad7c 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using Microsoft.CodeAnalysis.Serialization; using Microsoft.CodeAnalysis.Shared.Extensions; using Roslyn.Utilities; @@ -138,7 +139,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(nameof(SolutionAttributes), this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.SolutionAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index e42e9288a1c3d..bc873f7ff2f64 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -16,7 +16,7 @@ public SolutionStateChecksums(Checksum infoChecksum, ProjectChecksumCollection p { } - public SolutionStateChecksums(params object[] children) : base(nameof(SolutionStateChecksums), children) + public SolutionStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.SolutionStateChecksums, children) { } @@ -94,7 +94,7 @@ public ProjectStateChecksums( { } - public ProjectStateChecksums(params object[] children) : base(nameof(ProjectStateChecksums), children) + public ProjectStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.ProjectStateChecksums, children) { } @@ -228,7 +228,7 @@ public DocumentStateChecksums(Checksum infoChecksum, Checksum textChecksum) : { } - public DocumentStateChecksums(params object[] children) : base(nameof(DocumentStateChecksums), children) + public DocumentStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.DocumentStateChecksums, children) { } diff --git a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs index c9f89b7fb9bac..69a44e59f0e45 100644 --- a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs +++ b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs @@ -111,8 +111,8 @@ await VerifyAssetSerializationAsync( internal static async Task VerifyAssetSerializationAsync( ISolutionSynchronizationService service, Checksum checksum, - string kind, - Func assetGetter) + WellKnownSynchronizationKinds kind, + Func assetGetter) { // re-create asset from object var syncService = (SolutionSynchronizationServiceFactory.Service)service; @@ -219,7 +219,7 @@ internal static void VerifySnapshotInService( VerifyCollectionInService(snapshotService, projectObject.AdditionalDocuments.ToDocumentObjects(snapshotService), expectedAdditionalDocumentCount); } - internal static void VerifyCollectionInService(ISolutionSynchronizationService snapshotService, ChecksumCollection checksums, int expectedCount, string expectedItemKind) + internal static void VerifyCollectionInService(ISolutionSynchronizationService snapshotService, ChecksumCollection checksums, int expectedCount, WellKnownSynchronizationKinds expectedItemKind) { VerifyChecksumInService(snapshotService, checksums.Checksum, checksums.GetWellKnownSynchronizationKind()); Assert.Equal(checksums.Count, expectedCount); @@ -253,7 +253,7 @@ internal static void VerifySynchronizationObjectInService(ISolutionSynchroniz VerifyChecksumInService(snapshotService, syncObject.Checksum, syncObject.Kind); } - internal static void VerifyChecksumInService(ISolutionSynchronizationService snapshotService, Checksum checksum, string kind) + internal static void VerifyChecksumInService(ISolutionSynchronizationService snapshotService, Checksum checksum, WellKnownSynchronizationKinds kind) { Assert.NotNull(checksum); var otherObject = snapshotService.GetRemotableData(checksum, CancellationToken.None); @@ -266,7 +266,7 @@ internal static void SynchronizationObjectEqual(T checksumObject1, T checksum ChecksumEqual(checksumObject1.Checksum, checksumObject1.Kind, checksumObject2.Checksum, checksumObject2.Kind); } - internal static void ChecksumEqual(Checksum checksum1, string kind1, Checksum checksum2, string kind2) + internal static void ChecksumEqual(Checksum checksum1, WellKnownSynchronizationKinds kind1, Checksum checksum2, WellKnownSynchronizationKinds kind2) { Assert.Equal(checksum1, checksum2); Assert.Equal(kind1, kind2); diff --git a/src/Workspaces/Remote/Core/Services/AssetService.cs b/src/Workspaces/Remote/Core/Services/AssetService.cs index 26d7b536b9023..fdb46cce105d4 100644 --- a/src/Workspaces/Remote/Core/Services/AssetService.cs +++ b/src/Workspaces/Remote/Core/Services/AssetService.cs @@ -29,7 +29,7 @@ public AssetService(int sessionId, AssetStorage assetStorage) _assetStorage = assetStorage; } - public T Deserialize(string kind, ObjectReader reader, CancellationToken cancellationToken) + public T Deserialize(WellKnownSynchronizationKinds kind, ObjectReader reader, CancellationToken cancellationToken) { return s_serializer.Deserialize(kind, reader, cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs index 882b56a148972..391369afa7c0a 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; +using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger; @@ -72,7 +73,7 @@ private IList> ReadAssets( var responseChecksum = Checksum.ReadFrom(reader); Contract.ThrowIfFalse(checksums.Contains(responseChecksum)); - var kind = reader.ReadString(); + var kind = (WellKnownSynchronizationKinds)reader.ReadInt32(); // in service hub, cancellation means simply closed stream var @object = _owner.RoslynServices.AssetService.Deserialize(kind, reader, cancellationToken); From de0b9b01a3884909916763f6773064365dfc26a2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 16:11:01 -0700 Subject: [PATCH 176/214] Fixx test. --- src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs index 48e91cafcf691..e397a96b84349 100644 --- a/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/ObjectSerializationTests.cs @@ -959,6 +959,7 @@ private static void TestReadingPrimitiveValues(ObjectReader reader) Assert.Equal("\uDC00\uD800", (String)reader.ReadValue()); // invalid surrogate pair Assert.Equal("\uD800", (String)reader.ReadValue()); // incomplete surrogate pair Assert.Equal(null, reader.ReadValue()); + Assert.Equal(null, reader.ReadValue()); unchecked { From 68f4077e5df712dd78828e34fb9e81707204c755 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 17:23:14 -0700 Subject: [PATCH 177/214] Remove forced unecessary asynchrony in flushing data to disk. --- .../SQLite/SQLitePersistentStorage_Helpers.cs | 8 +-- .../SQLitePersistentStorage_WriteBatching.cs | 66 +++++++++++-------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs index 86d448098498c..b8523d652faa9 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs @@ -72,10 +72,10 @@ private static void CopyTo(Stream stream, byte[] bytes, int length) } /// - /// Amount of time to wait between flushing writes to disk. 250ms means we can flush - /// writes to disk four times a second. + /// Amount of time to wait between flushing writes to disk. 500ms means we can flush + /// writes to disk twp times a second. /// - private const int FlushAllDelayMS = 250; + private const int FlushAllDelayMS = 500; /// /// We use a pool to cache reads/writes that are less than 4k. Testing with Roslyn, @@ -89,7 +89,7 @@ private static void CopyTo(Stream stream, byte[] bytes, int length) /// /// The max amount of byte[]s we cache. This caps our cache at 4MB while allowing /// us to massively speed up writing (by batching writes). Because we can write to - /// disk 4 times a second. That means a total of 16MB/s that can be written to disk + /// disk two times a second. That means a total of 8MB/s that can be written to disk /// using only our cache. Given that Roslyn itself only writes about 50MB to disk /// after several minutes of analysis, this amount of bandwidth is more than sufficient. /// diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index f2e3633283c5b..a6d2642944eab 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.SQLite.Interop; @@ -72,16 +73,38 @@ private async Task FlushSpecificWritesAsync( ArrayBuilder> writesToProcess, CancellationToken cancellationToken) { - // Get the task that is responsible for doing the writes for this queue. - // This task will complete when all previously enqueued writes for this queue - // complete, and all the currently enqueued writes for this queue complete as well. - var writeTask = await GetWriteTaskAsync().ConfigureAwait(false); - await writeTask.ConfigureAwait(false); + // Get's the task representing the current writes being performed by another + // thread for this queue+key, and a TaskCompletionSource we can use to let + // other threads know about our own progress writing any new writes in this queue. + var (previousWritesTask, taskCompletionSource) = await GetWriteTaskAsync().ConfigureAwait(false); + + // Wait for all previous writes to be flushed. + await previousWritesTask.ConfigureAwait(false); + + if (writesToProcess.Count == 0) + { + // No additional writes for us to flush. We can immediately bail out. + Debug.Assert(taskCompletionSource == null); + return; + } + + // Now, if we have writes of our own, do them on this thread. + // + // Note: this flushing is not cancellable. We've already removed the + // writes from the write queue. If we were not to write them out we + // would be losing data. + Debug.Assert(taskCompletionSource != null); + + ProcessWriteQueue(connection, writesToProcess); + + // Mark our TCS as completed. Any other threads waiting on us will now be able + // to proceed. + taskCompletionSource.TrySetResult(0); return; // Local functions - async Task GetWriteTaskAsync() + async Task<(Task previousTask, TaskCompletionSource taskCompletionSource)> GetWriteTaskAsync() { // Have to acquire the semaphore. We're going to mutate the shared 'keyToWriteActions' // and 'keyToWriteTask' collections. @@ -106,30 +129,17 @@ async Task GetWriteTaskAsync() // We have no writes of our own. But there may be an existing task that // is writing out this queue. Return this so our caller can wait for // all existing writes to complete. - return existingWriteTask; + return (previousTask: existingWriteTask, taskCompletionSource: null); } - // We have our own writes to process. Enqueue the task to write - // these out after the existing write-task for this queue completes. - // - // We're currently under a lock, so tell the continuation to run - // *asynchronously* so that the TPL does not try to execute it inline - // with this thread. - // - // Note: this flushing is not cancellable. We've already removed the - // writes from the write queue. If we were not to write them out we - // would be losing data. - var nextTask = existingWriteTask.ContinueWith( - _ => ProcessWriteQueue(connection, writesToProcess), - CancellationToken.None, - TaskContinuationOptions.RunContinuationsAsynchronously, - TaskScheduler.Default); - - // Store this for the next flush call to see. - keyToWriteTask[key] = nextTask; - - // And return this to our caller so it can 'await' all these writes completing. - return nextTask; + // Create a TCS that represents our own work writing out "writesToProcess". + // Store it in keyToWriteTask so that if other threads come along, they'll + // wait for us to complete before doing their own reads/writes on this queue. + var localCompletionSource = new TaskCompletionSource(); + + keyToWriteTask[key] = localCompletionSource.Task; + + return (previousTask: existingWriteTask, taskCompletionSource: localCompletionSource); } } } From a8258312e2e9334d30091468a74519f9d302be63 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 17:29:23 -0700 Subject: [PATCH 178/214] Spelling. --- .../Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs index b8523d652faa9..ee63e5fe77c28 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_Helpers.cs @@ -73,7 +73,7 @@ private static void CopyTo(Stream stream, byte[] bytes, int length) /// /// Amount of time to wait between flushing writes to disk. 500ms means we can flush - /// writes to disk twp times a second. + /// writes to disk two times a second. /// private const int FlushAllDelayMS = 500; From 3ac8cb3eda96d994380df96d789daf07a3e0eb44 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 19:59:35 -0700 Subject: [PATCH 179/214] Defer creating a compilation if it is not needed. --- .../FindSymbols/SymbolTree/SymbolTreeInfo.cs | 8 +++++- .../SymbolTree/SymbolTreeInfo_Metadata.cs | 6 ++--- .../SymbolTreeInfo_Serialization.cs | 25 ++++++++----------- .../SymbolTree/SymbolTreeInfo_Source.cs | 18 ++++++------- 4 files changed, 28 insertions(+), 29 deletions(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs index 7284be192a654..b7bd6d9184ed3 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo.cs @@ -323,7 +323,13 @@ private static Task GetSpellCheckerTask( // for non-fuzzy searches, and soon afterwards it will be able to perform // fuzzy searches as well. return Task.Run(() => LoadOrCreateSpellCheckerAsync(solution, checksum, filePath, - () => new SpellChecker(checksum, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan))))); + () => CreateSpellCheckerAsync(checksum, concatenatedNames, sortedNodes))); + } + + private static Task CreateSpellCheckerAsync(Checksum checksum, string concatenatedNames, Node[] sortedNodes) + { + return Task.FromResult(new SpellChecker( + checksum, sortedNodes.Select(n => new StringSlice(concatenatedNames, n.NameSpan)))); } private static void SortNodes( diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs index 269128f523139..680635d5627ea 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Metadata.cs @@ -161,20 +161,20 @@ private static Task LoadOrCreateMetadataSymbolTreeInfoAsync( checksum, filePath, loadOnly, - create: () => CreateMetadataSymbolTreeInfo(solution, checksum, reference, cancellationToken), + createAsync: () => CreateMetadataSymbolTreeInfoAsync(solution, checksum, reference, cancellationToken), keySuffix: "_Metadata", getPersistedChecksum: info => info.Checksum, readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), cancellationToken: cancellationToken); } - private static SymbolTreeInfo CreateMetadataSymbolTreeInfo( + private static Task CreateMetadataSymbolTreeInfoAsync( Solution solution, Checksum checksum, PortableExecutableReference reference, CancellationToken cancellationToken) { var creator = new MetadataInfoCreator(solution, checksum, reference, cancellationToken); - return creator.Create(); + return Task.FromResult(creator.Create()); } private struct MetadataInfoCreator : IDisposable diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs index bcc01dfa6b1f7..d7a9b48c474f5 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Serialization.cs @@ -24,22 +24,17 @@ internal partial class SymbolTreeInfo : IObjectWritable /// info can't be loaded, it will be created (and persisted if possible). /// private static Task LoadOrCreateSourceSymbolTreeInfoAsync( - Solution solution, - IAssemblySymbol assembly, - Checksum checksum, - string filePath, - bool loadOnly, - CancellationToken cancellationToken) + Project project, Checksum checksum, bool loadOnly, CancellationToken cancellationToken) { return LoadOrCreateAsync( - solution, + project.Solution, checksum, - filePath, + project.FilePath, loadOnly, - create: () => CreateSourceSymbolTreeInfo(solution, checksum, assembly, filePath, cancellationToken), + createAsync: () => CreateSourceSymbolTreeInfoAsync(project, checksum, cancellationToken), keySuffix: "_Source", getPersistedChecksum: info => info.Checksum, - readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(solution, checksum, filePath, names, nodes)), + readObject: reader => ReadSymbolTreeInfo(reader, (names, nodes) => GetSpellCheckerTask(project.Solution, checksum, project.FilePath, names, nodes)), cancellationToken: cancellationToken); } @@ -51,14 +46,14 @@ private static Task LoadOrCreateSpellCheckerAsync( Solution solution, Checksum checksum, string filePath, - Func create) + Func> createAsync) { return LoadOrCreateAsync( solution, checksum, filePath, loadOnly: false, - create: create, + createAsync: createAsync, keySuffix: "_SpellChecker", getPersistedChecksum: s => s.Checksum, readObject: SpellChecker.ReadFrom, @@ -74,7 +69,7 @@ private static async Task LoadOrCreateAsync( Checksum checksum, string filePath, bool loadOnly, - Func create, + Func> createAsync, string keySuffix, Func getPersistedChecksum, Func readObject, @@ -82,7 +77,7 @@ private static async Task LoadOrCreateAsync( { if (checksum == null) { - return loadOnly ? null : create(); + return loadOnly ? null : await createAsync().ConfigureAwait(false); } // Ok, we can use persistence. First try to load from the persistence service. @@ -120,7 +115,7 @@ private static async Task LoadOrCreateAsync( } // Now, try to create a new instance and write it to the persistence service. - result = create(); + result = await createAsync().ConfigureAwait(false); if (result != null) { using (var stream = SerializableBytes.CreateWritableStream()) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 3993bef464d00..e32b24d41c07e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -28,14 +28,11 @@ private static void FreeSymbolMap(MultiDictionary symbolMap) s_symbolMapPool.Free(symbolMap); } - public static async Task GetInfoForSourceAssemblyAsync( + public static Task GetInfoForSourceAssemblyAsync( Project project, Checksum checksum, CancellationToken cancellationToken) { - var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - - return await LoadOrCreateSourceSymbolTreeInfoAsync( - project.Solution, compilation.Assembly, checksum, project.FilePath, - loadOnly: false, cancellationToken: cancellationToken).ConfigureAwait(false); + return LoadOrCreateSourceSymbolTreeInfoAsync( + project, checksum, loadOnly: false, cancellationToken: cancellationToken); } public static async Task GetSourceSymbolsChecksumAsync(Project project, CancellationToken cancellationToken) @@ -75,10 +72,11 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project } } - internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( - Solution solution, Checksum checksum, IAssemblySymbol assembly, - string filePath, CancellationToken cancellationToken) + internal static async Task CreateSourceSymbolTreeInfoAsync( + Project project, Checksum checksum, CancellationToken cancellationToken) { + var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); + var assembly = compilation.Assembly; if (assembly == null) { return null; @@ -90,7 +88,7 @@ internal static SymbolTreeInfo CreateSourceSymbolTreeInfo( GenerateSourceNodes(assembly.GlobalNamespace, unsortedNodes, s_getMembersNoPrivate); return CreateSymbolTreeInfo( - solution, checksum, filePath, unsortedNodes.ToImmutableAndFree(), + project.Solution, checksum, project.FilePath, unsortedNodes.ToImmutableAndFree(), inheritanceMap: new OrderPreservingMultiDictionary()); } From d712cc2ee913d4df8aa9dc8d80df8e4c76faa64d Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 20:17:54 -0700 Subject: [PATCH 180/214] Update test --- src/Workspaces/CoreTest/FindAllDeclarationsTests.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs index 193edcce5cada..af5f62459c334 100644 --- a/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs +++ b/src/Workspaces/CoreTest/FindAllDeclarationsTests.cs @@ -537,13 +537,11 @@ await Assert.ThrowsAnyAsync(async () => public async Task TestSymbolTreeInfoSerialization() { var solution = GetSolution(WorkspaceKind.SingleClass); - var compilation = await solution.Projects.First().GetCompilationAsync(); - var assembly = compilation.GetSpecialType(SpecialType.System_Byte).ContainingAssembly; - ////var assembly = compilation.Assembly; + var project = solution.Projects.First(); // create symbol tree info from assembly - var info = SymbolTreeInfo.CreateSourceSymbolTreeInfo( - solution, Checksum.Null, assembly, "", cancellationToken: CancellationToken.None); + var info = await SymbolTreeInfo.CreateSourceSymbolTreeInfoAsync( + project, Checksum.Null, cancellationToken: CancellationToken.None); using (var writerStream = new MemoryStream()) { From 31d0de2926b5c0b37520b5bc23d143cc89d4c6b7 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 21:12:23 -0700 Subject: [PATCH 181/214] Ensure documents are ordered before making checksum. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index e32b24d41c07e..8eccd85b53bfd 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -46,7 +46,7 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project var serializer = new Serializer(project.Solution.Workspace); var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); - var textChecksumsTasks = project.Documents.Select(async d => + var textChecksumsTasks = project.Documents.OrderBy(d => d.FilePath).Select(async d => { var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); return documentStateChecksum.Text; From 618fd16cf3c547ea9b49f245ddd6d45820e9e9a1 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 21:18:22 -0700 Subject: [PATCH 182/214] Add comment. --- .../Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index 8eccd85b53bfd..ef70350e1803e 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -46,6 +46,8 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project var serializer = new Serializer(project.Solution.Workspace); var projectStateChecksums = await project.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); + // Order the documents by FilePath. Default ordering in the RemoteWorkspace is + // to be ordered by Guid (which is not consistent across VS sessions). var textChecksumsTasks = project.Documents.OrderBy(d => d.FilePath).Select(async d => { var documentStateChecksum = await d.State.GetStateChecksumsAsync(cancellationToken).ConfigureAwait(false); From 6e7fbe80e72d3e3e84f7e324666ddeaedb4cac54 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 22:36:15 -0700 Subject: [PATCH 183/214] Formatting. --- src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs b/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs index 16c5e418ab109..75cf688e7a575 100644 --- a/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs +++ b/src/Compilers/Core/Portable/Diagnostic/Diagnostic.cs @@ -362,7 +362,7 @@ public bool IsWarningAsError /// if there is no entry. This can be used to put diagnostic specific information you want /// to pass around. for example, to corresponding fixer. /// - public virtual ImmutableDictionary Properties + public virtual ImmutableDictionary Properties => ImmutableDictionary.Empty; string IFormattable.ToString(string ignored, IFormatProvider formatProvider) @@ -516,4 +516,4 @@ internal abstract class RequiredLanguageVersion : IMessageSerializable { public abstract override string ToString(); } -} +} \ No newline at end of file From 4d3d313e3f76faf470c794f67e6319166f551aef Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Sun, 7 May 2017 00:43:19 -0700 Subject: [PATCH 184/214] Ignoring compiler warnings --- src/CodeStyle/VisualBasic/Analyzers/BasicCodeStyle.vbproj | 1 + src/CodeStyle/VisualBasic/CodeFixes/BasicCodeStyleFixes.vbproj | 1 + src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj | 1 + src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj | 2 +- .../Source/ExpressionCompiler/BasicExpressionCompiler.vbproj | 1 + .../ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj | 1 + .../ResultProvider/Portable/BasicResultProvider.Portable.vbproj | 1 + src/Features/VisualBasic/Portable/BasicFeatures.vbproj | 1 + src/Interactive/VbiCore/VbiCore.vbproj | 1 + src/Scripting/VisualBasic/BasicScripting.vbproj | 1 + src/Scripting/VisualBasicTest/BasicScriptingTest.vbproj | 1 + .../VisualBasicErrorFactsGenerator.vbproj | 1 + .../VisualBasicSyntaxGenerator.vbproj | 1 + src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj | 1 + 14 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/CodeStyle/VisualBasic/Analyzers/BasicCodeStyle.vbproj b/src/CodeStyle/VisualBasic/Analyzers/BasicCodeStyle.vbproj index cfb607d10d63b..808ef60935971 100644 --- a/src/CodeStyle/VisualBasic/Analyzers/BasicCodeStyle.vbproj +++ b/src/CodeStyle/VisualBasic/Analyzers/BasicCodeStyle.vbproj @@ -10,6 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic.CodeStyle netstandard1.3 portable-net45+win8;dotnet + $(NoWarn);40057 diff --git a/src/CodeStyle/VisualBasic/CodeFixes/BasicCodeStyleFixes.vbproj b/src/CodeStyle/VisualBasic/CodeFixes/BasicCodeStyleFixes.vbproj index 8be31b2375f70..0723c7240362b 100644 --- a/src/CodeStyle/VisualBasic/CodeFixes/BasicCodeStyleFixes.vbproj +++ b/src/CodeStyle/VisualBasic/CodeFixes/BasicCodeStyleFixes.vbproj @@ -10,6 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic.CodeStyle.Fixes netstandard1.3 portable-net45+win8;dotnet + $(NoWarn);40057 diff --git a/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj b/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj index d4167746d1b00..dd58c86df3fd1 100644 --- a/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj +++ b/src/CodeStyle/VisualBasic/Tests/BasicCodeStyleTests.vbproj @@ -12,6 +12,7 @@ netstandard1.3 portable-net45+win8;dotnet UnitTestPortable + $(NoWarn);40057 diff --git a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj index aef1ff02a7a12..01c41ca224d90 100644 --- a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj +++ b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj @@ -10,7 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic netstandard1.3 true - $(NoWarn);42014 + $(NoWarn);42014;40057;42016;41999 ..\BasicCodeAnalysisRules.ruleset diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj index 973c1b53661b3..0b1a76f72bf1c 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/NetFX20/BasicResultProvider.NetFX20.vbproj @@ -12,6 +12,7 @@ Library Microsoft.CodeAnalysis.VisualBasic.ExpressionEvaluator.ResultProvider net20 + $(NoWarn);40057 diff --git a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/BasicResultProvider.Portable.vbproj b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/BasicResultProvider.Portable.vbproj index 7344f2e4c6ae3..927ec6ffb56a3 100644 --- a/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/BasicResultProvider.Portable.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Source/ResultProvider/Portable/BasicResultProvider.Portable.vbproj @@ -11,6 +11,7 @@ netstandard1.3 portable-net45+win8 false + $(NoWarn);40057 diff --git a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj index 6bfd88518e196..352ccc1c9746a 100644 --- a/src/Features/VisualBasic/Portable/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/Portable/BasicFeatures.vbproj @@ -10,6 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic.Features netstandard1.3 portable-net45+win8;dotnet + $(NoWarn);40057 diff --git a/src/Interactive/VbiCore/VbiCore.vbproj b/src/Interactive/VbiCore/VbiCore.vbproj index 1173ab452732c..24da7ac09ae0b 100644 --- a/src/Interactive/VbiCore/VbiCore.vbproj +++ b/src/Interactive/VbiCore/VbiCore.vbproj @@ -16,6 +16,7 @@ win7-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;osx.10.12-x64 portable-net452 true + $(NoWarn);40057 diff --git a/src/Scripting/VisualBasic/BasicScripting.vbproj b/src/Scripting/VisualBasic/BasicScripting.vbproj index 2cf39f64ce46d..51cda3aa86ee9 100644 --- a/src/Scripting/VisualBasic/BasicScripting.vbproj +++ b/src/Scripting/VisualBasic/BasicScripting.vbproj @@ -10,6 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic.Scripting netstandard1.3 portable-net452 + $(NoWarn);40057 diff --git a/src/Scripting/VisualBasicTest/BasicScriptingTest.vbproj b/src/Scripting/VisualBasicTest/BasicScriptingTest.vbproj index 18f8eefa40256..a7e851f98705d 100644 --- a/src/Scripting/VisualBasicTest/BasicScriptingTest.vbproj +++ b/src/Scripting/VisualBasicTest/BasicScriptingTest.vbproj @@ -12,6 +12,7 @@ netstandard1.3 portable-net452 UnitTestDesktop + $(NoWarn);40057;42016 diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj index 01303056087b6..bf950924681ed 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicErrorFactsGenerator/VisualBasicErrorFactsGenerator.vbproj @@ -16,6 +16,7 @@ netcoreapp1.1 win7-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;osx.10.12-x64 true + $(NoWarn);40057 diff --git a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj index fd78b5813f6f5..9aa9b9d8967e7 100644 --- a/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj +++ b/src/Tools/Source/CompilerGeneratorTools/Source/VisualBasicSyntaxGenerator/VisualBasicSyntaxGenerator.vbproj @@ -17,6 +17,7 @@ netcoreapp1.1 win7-x64;ubuntu.14.04-x64;ubuntu.16.04-x64;osx.10.12-x64 true + $(NoWarn);40057 diff --git a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj index d7bbf9fdc831e..db2cf36f964f2 100644 --- a/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj +++ b/src/Workspaces/VisualBasic/Portable/BasicWorkspace.vbproj @@ -11,6 +11,7 @@ netstandard1.3 portable-net45+win8;dotnet true + $(NoWarn);40057 From b4fc03602e2d197b7a0b3a45eda5deec7b804eba Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Sun, 7 May 2017 04:01:41 -0700 Subject: [PATCH 185/214] fixing implicit comversion errors --- .../VisualBasic/Portable/BasicCodeAnalysis.vbproj | 2 +- .../Portable/Compilation/VisualBasicCompilation.vb | 4 ++-- .../Emit/EditAndContinue/VisualBasicSymbolMatcher.vb | 2 +- .../Portable/Symbols/Source/SourceLambdaSymbol.vb | 2 +- .../Portable/Symbols/Source/SourceMethodSymbol.vb | 2 +- .../SynthesizedSymbols/SynthesizedMethodBase.vb | 2 +- .../VisualBasicTest/BasicScriptingTest.vbproj | 2 +- .../VisualBasicTest/InteractiveSessionTests.vb | 12 ++++++------ src/Scripting/VisualBasicTest/ScriptTests.vb | 10 +++++----- 9 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj index 01c41ca224d90..c0c40fb4981fb 100644 --- a/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj +++ b/src/Compilers/VisualBasic/Portable/BasicCodeAnalysis.vbproj @@ -10,7 +10,7 @@ Microsoft.CodeAnalysis.VisualBasic netstandard1.3 true - $(NoWarn);42014;40057;42016;41999 + $(NoWarn);42014;40057 ..\BasicCodeAnalysisRules.ruleset @@ -423,16 +425,24 @@ - + + + + ConsoleToMSBuild="true" + Condition="Exists('$(IbcMergePath)')"> diff --git a/build/Targets/Packages.props b/build/Targets/Packages.props index 4a9555033cd19..d359d97267662 100644 --- a/build/Targets/Packages.props +++ b/build/Targets/Packages.props @@ -39,6 +39,7 @@ 1.0.0-beta1-61618-01 1.5.0 1.2.0 + 4.7.1-alpha-00001 3.13.8 15.0.26201-alpha 14.3.25407-alpha diff --git a/build/ToolsetPackages/InternalToolset.csproj b/build/ToolsetPackages/InternalToolset.csproj new file mode 100644 index 0000000000000..d98fca0cc8dcf --- /dev/null +++ b/build/ToolsetPackages/InternalToolset.csproj @@ -0,0 +1,10 @@ + + + + + net46 + + + + + From b58ba7232f173335e76567231f08a71fa86599b6 Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Thu, 4 May 2017 17:40:49 -0700 Subject: [PATCH 203/214] Restore developer build desktop deployment --- build/Targets/Imports.targets | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/build/Targets/Imports.targets b/build/Targets/Imports.targets index 34a04750b86e6..44952ff79bcb4 100644 --- a/build/Targets/Imports.targets +++ b/build/Targets/Imports.targets @@ -450,22 +450,18 @@ - - - + Condition="'$(_IsAnyPortableUnitTest)' == 'true' AND '$(DeveloperBuild)' == 'true' AND '$(OS)' == 'Windows_NT'"> + + + + + + + + + + + From 911adf53d971518799d91aee8c044e6733250336 Mon Sep 17 00:00:00 2001 From: Nick Guerrera Date: Fri, 5 May 2017 09:07:47 -0700 Subject: [PATCH 204/214] Use Target Returns instead of Target Outputs The former always has the meaning we want, the latter has it only when there are no Returns used anywhere in the same file! --- build/Targets/Imports.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Targets/Imports.targets b/build/Targets/Imports.targets index 44952ff79bcb4..6a54279474a86 100644 --- a/build/Targets/Imports.targets +++ b/build/Targets/Imports.targets @@ -493,7 +493,7 @@ Returns="@(ReferenceCopyLocalPaths)" /> - + diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj index ab92292b3745e..920963a1e6a85 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj @@ -11,6 +11,7 @@ Roslyn.ExpressionEvaluator.VisualBasic.ExpressionCompiler.UnitTests v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj index 7e3fb7a31760a..f7c9c351216c1 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj @@ -12,6 +12,7 @@ v4.6 win7 Default + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Interactive/HostTest/InteractiveHostTest.csproj b/src/Interactive/HostTest/InteractiveHostTest.csproj index b3f7f90eba0e8..5957aa836ebaa 100644 --- a/src/Interactive/HostTest/InteractiveHostTest.csproj +++ b/src/Interactive/HostTest/InteractiveHostTest.csproj @@ -12,6 +12,7 @@ Roslyn.InteractiveHost.UnitTests v4.6 win7-x86 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj b/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj index e10fe785d2efe..02ed8006fcac7 100644 --- a/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj +++ b/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj @@ -13,6 +13,7 @@ ConvertToConditionalCS.UnitTests v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj b/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj index f62dcf4a0e1e7..905d42783d381 100644 --- a/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj +++ b/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj @@ -14,6 +14,7 @@ ImplementNotifyPropertyChangedCS.UnitTests v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj b/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj index f6637440812e7..38791850a0b88 100644 --- a/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj @@ -14,6 +14,7 @@ Off v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest $(NoWarn);41999,42016,42030,42104,42108,42109 41998,42004,42020,42021,42022,42026,42029,42031,42105,42106,42107,42353,42354,42355 diff --git a/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj b/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj index 0a89d35f6c63c..3127b0910e9c1 100644 --- a/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj @@ -13,6 +13,7 @@ Default v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj b/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj index 7ac42b03a918c..85383222dfa60 100644 --- a/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj @@ -14,6 +14,7 @@ Off v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest $(NoWarn);41999,42016,42030,42104,42108,42109 41998,42004,42020,42021,42022,42026,42029,42031,42105,42106,42107,42353,42354,42355 diff --git a/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj b/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj index 41fef09765405..a56c71dd3478d 100644 --- a/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj +++ b/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj @@ -14,6 +14,7 @@ true v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj b/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj index cec3b5794b580..8e4eaa20828d7 100644 --- a/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj +++ b/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj @@ -14,6 +14,7 @@ true v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj b/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj index 43a6e0f83cdd3..107e463f600dc 100644 --- a/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj +++ b/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj @@ -13,6 +13,7 @@ true v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj index f036c9fa5b027..361d8be3b5cfd 100644 --- a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj +++ b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj @@ -13,6 +13,7 @@ v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj index 81da87450648e..f0bc240b45e25 100644 --- a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj +++ b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj @@ -14,6 +14,7 @@ win7 true + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index ada8ce72e8eb5..a303a5c87dd45 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -13,6 +13,7 @@ win7 true + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest true diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj index 258b08061d755..df269223b57a0 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -12,6 +12,7 @@ v4.6 win7-x86 true + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj index da29ffc1b5728..d4f64e8da45b9 100644 --- a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj +++ b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj @@ -12,6 +12,7 @@ Roslyn.Services.CSharp.UnitTests v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/CoreTest/ServicesTest.csproj b/src/Workspaces/CoreTest/ServicesTest.csproj index db824df8ea2a6..5f2d1246d47c2 100644 --- a/src/Workspaces/CoreTest/ServicesTest.csproj +++ b/src/Workspaces/CoreTest/ServicesTest.csproj @@ -12,6 +12,7 @@ Roslyn.Services.UnitTests v4.6 win7 + {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj b/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj index ed69a34b4b746..1fd0fccb24243 100644 --- a/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj +++ b/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj @@ -13,6 +13,7 @@ Default v4.6 win7 + {F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest From 98cc10ca002c9e90d40f79ce28bafbd2dacc76c9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 1 Apr 2017 05:44:35 -0500 Subject: [PATCH 206/214] Add the test project GUID to all non-portable test projects --- .../CSharp/Test/CommandLine/CSharpCommandLineTest.csproj | 2 +- src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj | 2 +- .../CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj | 2 +- src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj | 2 +- src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj | 2 +- src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj | 2 +- src/Compilers/Server/VBCSCompilerTests/VBCSCompilerTests.csproj | 2 +- .../VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj | 2 +- .../VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj | 2 +- .../VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj | 2 +- .../VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj | 2 +- .../VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj | 2 +- src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj | 2 +- src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj | 2 +- src/EditorFeatures/Test/EditorServicesTest.csproj | 2 +- src/EditorFeatures/Test2/EditorServicesTest2.vbproj | 2 +- .../VisualBasicTest/BasicEditorServicesTest.vbproj | 2 +- .../Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj | 2 +- .../CSharp/Test/ResultProvider/CSharpResultProviderTest.csproj | 2 +- .../Core/Test/FunctionResolver/FunctionResolverTest.csproj | 2 +- .../Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj | 2 +- .../Test/ResultProvider/BasicResultProviderTest.vbproj | 2 +- src/Interactive/HostTest/InteractiveHostTest.csproj | 2 +- .../Test/ConvertToConditionalCS.UnitTests.csproj | 2 +- .../Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj | 2 +- .../Test/ConvertToAutoPropertyVB.UnitTests.vbproj | 2 +- .../Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj | 2 +- .../VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj | 2 +- .../CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj | 2 +- src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj | 2 +- .../VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj | 2 +- src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj | 2 +- src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj | 2 +- src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj | 2 +- .../IntegrationTests/VisualStudioIntegrationTests.csproj | 2 +- src/Workspaces/CSharpTest/CSharpServicesTest.csproj | 2 +- src/Workspaces/CoreTest/ServicesTest.csproj | 2 +- src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj | 2 +- 38 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj index b40277a2da4c4..7330f6a7e6007 100644 --- a/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj +++ b/src/Compilers/CSharp/Test/CommandLine/CSharpCommandLineTest.csproj @@ -13,7 +13,7 @@ false v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj index e8b665ec58ba2..0d8951495e9e2 100644 --- a/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj +++ b/src/Compilers/CSharp/Test/Emit/CSharpCompilerEmitTest.csproj @@ -12,7 +12,7 @@ Roslyn.Compilers.CSharp.Emit.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest true diff --git a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj index 0620301ce164a..3a4ee46c11d35 100644 --- a/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj +++ b/src/Compilers/CSharp/Test/Semantic/CSharpCompilerSemanticTest.csproj @@ -13,7 +13,7 @@ false v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj b/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj index 7a3e671e48607..d4a8fa9b43ca1 100644 --- a/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj +++ b/src/Compilers/CSharp/Test/WinRT/CSharpWinRTTest.csproj @@ -13,7 +13,7 @@ Roslyn.Compilers.CSharp.WinRT.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj index bbf38f77dcd2f..d372a75ba2adc 100644 --- a/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj +++ b/src/Compilers/Core/CodeAnalysisTest/CodeAnalysisTest.csproj @@ -13,7 +13,7 @@ true v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj index e7739d119f51e..5747f09212a03 100644 --- a/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj +++ b/src/Compilers/Core/MSBuildTaskTests/MSBuildTaskTests.csproj @@ -13,7 +13,7 @@ true v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerTests.csproj b/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerTests.csproj index 5ff8ea53fc3a4..22be369046014 100644 --- a/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerTests.csproj +++ b/src/Compilers/Server/VBCSCompilerTests/VBCSCompilerTests.csproj @@ -11,7 +11,7 @@ Microsoft.CodeAnalysis.CompilerServer.UnitTests Roslyn.Compilers.CompilerServer.UnitTests true - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} v4.6 win7 UnitTest diff --git a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj index a09b0910a53cf..3e1744bafea98 100644 --- a/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj +++ b/src/Compilers/VisualBasic/Test/CommandLine/BasicCommandLineTest.vbproj @@ -12,7 +12,7 @@ false v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj b/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj index 7cbab57ca30d2..b63d0f9d64a4f 100644 --- a/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Emit/BasicCompilerEmitTest.vbproj @@ -11,7 +11,7 @@ Roslyn.Compilers.VisualBasic.Emit.UnitTests v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj index eacd200ce6067..44e47678f3728 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Semantic/BasicCompilerSemanticTest.vbproj @@ -11,7 +11,7 @@ Roslyn.Compilers.VisualBasic.Semantic.UnitTests v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj index 1679cbeb02a77..d522cbc0c274d 100644 --- a/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Symbol/BasicCompilerSymbolTest.vbproj @@ -11,7 +11,7 @@ Roslyn.Compilers.VisualBasic.Symbol.UnitTests v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj index 711966dfa6065..d2a065d42983b 100644 --- a/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj +++ b/src/Compilers/VisualBasic/Test/Syntax/BasicCompilerSyntaxTest.vbproj @@ -11,7 +11,7 @@ Roslyn.Compilers.VisualBasic.Syntax.UnitTests v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj index 78a0ee30f46ca..24d00d4664012 100644 --- a/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj +++ b/src/EditorFeatures/CSharpTest/CSharpEditorServicesTest.csproj @@ -146,7 +146,7 @@ Library Microsoft.CodeAnalysis.Editor.CSharp.UnitTests Roslyn.Services.Editor.CSharp.UnitTests - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj index 725a7a7e51415..ec235f71db4fb 100644 --- a/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj +++ b/src/EditorFeatures/CSharpTest2/CSharpEditorServicesTest2.csproj @@ -142,7 +142,7 @@ Library Microsoft.CodeAnalysis.Editor.CSharp.UnitTests Roslyn.Services.Editor.CSharp2.UnitTests - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/EditorFeatures/Test/EditorServicesTest.csproj b/src/EditorFeatures/Test/EditorServicesTest.csproj index 7664ee48375df..5fd0df6032926 100644 --- a/src/EditorFeatures/Test/EditorServicesTest.csproj +++ b/src/EditorFeatures/Test/EditorServicesTest.csproj @@ -13,7 +13,7 @@ v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/EditorFeatures/Test2/EditorServicesTest2.vbproj b/src/EditorFeatures/Test2/EditorServicesTest2.vbproj index c20023979a633..53a950f293939 100644 --- a/src/EditorFeatures/Test2/EditorServicesTest2.vbproj +++ b/src/EditorFeatures/Test2/EditorServicesTest2.vbproj @@ -13,7 +13,7 @@ v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index 812a02e7aeb84..5960dd18eb7f3 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -14,7 +14,7 @@ v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj index 93ebd7dde84a6..f59399c4d4b17 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/CSharpExpressionCompilerTest.csproj @@ -12,7 +12,7 @@ Roslyn.ExpressionEvaluator.CSharp.ExpressionCompiler.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest true diff --git a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/CSharpResultProviderTest.csproj b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/CSharpResultProviderTest.csproj index c47acddba1d8e..983e564b02121 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ResultProvider/CSharpResultProviderTest.csproj +++ b/src/ExpressionEvaluator/CSharp/Test/ResultProvider/CSharpResultProviderTest.csproj @@ -12,7 +12,7 @@ Roslyn.ExpressionEvaluator.CSharp.ResultProvider.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest true diff --git a/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj b/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj index 2e24fe6cdb0bd..73357b3711099 100644 --- a/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj +++ b/src/ExpressionEvaluator/Core/Test/FunctionResolver/FunctionResolverTest.csproj @@ -13,7 +13,7 @@ true v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj index 920963a1e6a85..b7846be22202e 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Test/ExpressionCompiler/BasicExpressionCompilerTest.vbproj @@ -11,7 +11,7 @@ Roslyn.ExpressionEvaluator.VisualBasic.ExpressionCompiler.UnitTests v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj index f7c9c351216c1..2545a1f138d1e 100644 --- a/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj +++ b/src/ExpressionEvaluator/VisualBasic/Test/ResultProvider/BasicResultProviderTest.vbproj @@ -12,7 +12,7 @@ v4.6 win7 Default - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Interactive/HostTest/InteractiveHostTest.csproj b/src/Interactive/HostTest/InteractiveHostTest.csproj index 5957aa836ebaa..9c0a27cb635bd 100644 --- a/src/Interactive/HostTest/InteractiveHostTest.csproj +++ b/src/Interactive/HostTest/InteractiveHostTest.csproj @@ -12,7 +12,7 @@ Roslyn.InteractiveHost.UnitTests v4.6 win7-x86 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj b/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj index 02ed8006fcac7..7b70313d09b41 100644 --- a/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj +++ b/src/Samples/CSharp/ConvertToConditional/Test/ConvertToConditionalCS.UnitTests.csproj @@ -13,7 +13,7 @@ ConvertToConditionalCS.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj b/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj index 905d42783d381..70ad8820d4b93 100644 --- a/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj +++ b/src/Samples/CSharp/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedCS.UnitTests.csproj @@ -14,7 +14,7 @@ ImplementNotifyPropertyChangedCS.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj b/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj index 38791850a0b88..88fdb29f6db85 100644 --- a/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/ConvertToAutoProperty/Test/ConvertToAutoPropertyVB.UnitTests.vbproj @@ -14,7 +14,7 @@ Off v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest $(NoWarn);41999,42016,42030,42104,42108,42109 41998,42004,42020,42021,42022,42026,42029,42031,42105,42106,42107,42353,42354,42355 diff --git a/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj b/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj index 3127b0910e9c1..6c278b644f011 100644 --- a/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/ImplementNotifyPropertyChanged/Test/ImplementNotifyPropertyChangedVB.UnitTests.vbproj @@ -13,7 +13,7 @@ Default v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj b/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj index 85383222dfa60..7a2db6f1b11af 100644 --- a/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj +++ b/src/Samples/VisualBasic/RemoveByVal/Test/RemoveByValVB.UnitTests.vbproj @@ -14,7 +14,7 @@ Off v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest $(NoWarn);41999,42016,42030,42104,42108,42109 41998,42004,42020,42021,42022,42026,42029,42031,42105,42106,42107,42353,42354,42355 diff --git a/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj b/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj index a56c71dd3478d..60f4d64c28c6b 100644 --- a/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj +++ b/src/Scripting/CSharpTest.Desktop/CSharpScriptingTest.Desktop.csproj @@ -14,7 +14,7 @@ true v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj b/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj index 8e4eaa20828d7..07f34a825aae8 100644 --- a/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj +++ b/src/Scripting/CoreTest.Desktop/ScriptingTest.Desktop.csproj @@ -14,7 +14,7 @@ true v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj b/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj index 107e463f600dc..01d549bc9fd58 100644 --- a/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj +++ b/src/Scripting/VisualBasicTest.Desktop/BasicScriptingTest.Desktop.vbproj @@ -13,7 +13,7 @@ true v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest diff --git a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj index 361d8be3b5cfd..34fed6c3a7c3e 100644 --- a/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj +++ b/src/VisualStudio/CSharp/Test/CSharpVisualStudioTest.csproj @@ -13,7 +13,7 @@ v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj index f0bc240b45e25..6cb0c1dfabd0c 100644 --- a/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj +++ b/src/VisualStudio/Core/Test.Next/VisualStudioTest.Next.csproj @@ -14,7 +14,7 @@ win7 true - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj index a303a5c87dd45..67d1e6fcf7af6 100644 --- a/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj +++ b/src/VisualStudio/Core/Test/ServicesVisualStudioTest.vbproj @@ -13,7 +13,7 @@ win7 true - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest true diff --git a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj index df269223b57a0..94fe76840afc8 100644 --- a/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj +++ b/src/VisualStudio/IntegrationTest/IntegrationTests/VisualStudioIntegrationTests.csproj @@ -12,7 +12,7 @@ v4.6 win7-x86 true - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj index d4f64e8da45b9..acdef49c089b4 100644 --- a/src/Workspaces/CSharpTest/CSharpServicesTest.csproj +++ b/src/Workspaces/CSharpTest/CSharpServicesTest.csproj @@ -12,7 +12,7 @@ Roslyn.Services.CSharp.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/CoreTest/ServicesTest.csproj b/src/Workspaces/CoreTest/ServicesTest.csproj index 5f2d1246d47c2..c89c53073539a 100644 --- a/src/Workspaces/CoreTest/ServicesTest.csproj +++ b/src/Workspaces/CoreTest/ServicesTest.csproj @@ -12,7 +12,7 @@ Roslyn.Services.UnitTests v4.6 win7 - {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTest diff --git a/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj b/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj index 1fd0fccb24243..1b33d5a7b8e87 100644 --- a/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj +++ b/src/Workspaces/VisualBasicTest/VisualBasicServicesTest.vbproj @@ -13,7 +13,7 @@ Default v4.6 win7 - {F184B08F-C81C-45F6-A57F-5ABD9991F28F} + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{F184B08F-C81C-45F6-A57F-5ABD9991F28F} UnitTest From 1d7c8484a7db9b7231de4618d0c0bb8c96d73fd7 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Sat, 1 Apr 2017 05:46:56 -0500 Subject: [PATCH 207/214] Add the UnitTestContainer capability to all portable test projects --- build/Targets/Imports.targets | 5 +++++ .../CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj | 1 - .../CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj | 1 - src/Scripting/CSharpTest/CSharpScriptingTest.csproj | 1 - src/Scripting/CoreTest/ScriptingTest.csproj | 1 - 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/build/Targets/Imports.targets b/build/Targets/Imports.targets index 6aaa705f52acc..459d17712bff7 100644 --- a/build/Targets/Imports.targets +++ b/build/Targets/Imports.targets @@ -41,6 +41,11 @@ false $(OutputPath)Dlls\$(MSBuildProjectName)\ + + + + + diff --git a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj index 3e0db85ec387d..ca3e12159add2 100644 --- a/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj +++ b/src/Compilers/CSharp/Test/Symbol/CSharpCompilerSymbolTest.csproj @@ -10,7 +10,6 @@ Library Microsoft.CodeAnalysis.CSharp.Symbol.UnitTests Roslyn.Compilers.CSharp.Symbol.UnitTests - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} netstandard1.3 portable-net452 true diff --git a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj index 22e0c724b896a..d84e80c52b9cc 100644 --- a/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj +++ b/src/Compilers/CSharp/Test/Syntax/CSharpCompilerSyntaxTest.csproj @@ -11,7 +11,6 @@ Microsoft.CodeAnalysis.CSharp.UnitTests Roslyn.Compilers.CSharp.Syntax.UnitTests false - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} netstandard1.3 portable-net452 true diff --git a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj index 8ca43a6381ac9..7c8e96aad5551 100644 --- a/src/Scripting/CSharpTest/CSharpScriptingTest.csproj +++ b/src/Scripting/CSharpTest/CSharpScriptingTest.csproj @@ -14,7 +14,6 @@ true netstandard1.3 portable-net452 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTestDesktop diff --git a/src/Scripting/CoreTest/ScriptingTest.csproj b/src/Scripting/CoreTest/ScriptingTest.csproj index 3e77db0b4d16e..88fec7f523ddf 100644 --- a/src/Scripting/CoreTest/ScriptingTest.csproj +++ b/src/Scripting/CoreTest/ScriptingTest.csproj @@ -14,7 +14,6 @@ true netstandard1.3 portable-net452 - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} UnitTestPortable From fadaba7dc7aea3d6a4cac16589a20e1e09d2487a Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 11:37:49 -0700 Subject: [PATCH 208/214] Be resilient to exceptions getting thrown. --- .../SQLitePersistentStorage_WriteBatching.cs | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs index a6d2642944eab..783d1ac68aecb 100644 --- a/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs +++ b/src/Workspaces/Core/Desktop/Workspace/SQLite/SQLitePersistentStorage_WriteBatching.cs @@ -77,29 +77,41 @@ private async Task FlushSpecificWritesAsync( // thread for this queue+key, and a TaskCompletionSource we can use to let // other threads know about our own progress writing any new writes in this queue. var (previousWritesTask, taskCompletionSource) = await GetWriteTaskAsync().ConfigureAwait(false); - - // Wait for all previous writes to be flushed. - await previousWritesTask.ConfigureAwait(false); - - if (writesToProcess.Count == 0) + try { - // No additional writes for us to flush. We can immediately bail out. - Debug.Assert(taskCompletionSource == null); - return; - } + // Wait for all previous writes to be flushed. + await previousWritesTask.ConfigureAwait(false); - // Now, if we have writes of our own, do them on this thread. - // - // Note: this flushing is not cancellable. We've already removed the - // writes from the write queue. If we were not to write them out we - // would be losing data. - Debug.Assert(taskCompletionSource != null); + if (writesToProcess.Count == 0) + { + // No additional writes for us to flush. We can immediately bail out. + Debug.Assert(taskCompletionSource == null); + return; + } - ProcessWriteQueue(connection, writesToProcess); + // Now, if we have writes of our own, do them on this thread. + // + // Note: this flushing is not cancellable. We've already removed the + // writes from the write queue. If we were not to write them out we + // would be losing data. + Debug.Assert(taskCompletionSource != null); - // Mark our TCS as completed. Any other threads waiting on us will now be able - // to proceed. - taskCompletionSource.TrySetResult(0); + ProcessWriteQueue(connection, writesToProcess); + } + catch (OperationCanceledException ex) + { + taskCompletionSource?.TrySetCanceled(ex.CancellationToken); + } + catch (Exception ex) + { + taskCompletionSource?.TrySetException(ex); + } + finally + { + // Mark our TCS as completed. Any other threads waiting on us will now be able + // to proceed. + taskCompletionSource?.TrySetResult(0); + } return; From 3ffbbd837e9414204330835320fc27e2ec3108d2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 11:50:19 -0700 Subject: [PATCH 209/214] Rename type. --- .../Core/Portable/Execution/CustomAsset.cs | 8 +- .../Portable/Execution/CustomAssetBuilder.cs | 2 +- .../Core/Portable/Execution/Extensions.cs | 42 +++++----- .../Core/Portable/Execution/RemotableData.cs | 8 +- .../Core/Portable/Execution/Serializer.cs | 76 +++++++++---------- .../Serializer_ChecksumWithChildren.cs | 26 +++---- .../Core/Portable/Execution/SolutionAsset.cs | 4 +- ...nds.cs => WellKnownSynchronizationKind.cs} | 2 +- .../SymbolTree/SymbolTreeInfo_Source.cs | 2 +- .../SyntaxTree/SyntaxTreeIndex_Persistence.cs | 2 +- .../Workspace/Solution/ChecksumCollection.cs | 16 ++-- .../Solution/ChecksumWithChildren.cs | 4 +- .../Workspace/Solution/Checksum_Factory.cs | 8 +- .../Workspace/Solution/DocumentInfo.cs | 2 +- .../Workspace/Solution/ProjectInfo.cs | 2 +- .../Workspace/Solution/SolutionInfo.cs | 2 +- .../Workspace/Solution/StateChecksums.cs | 6 +- .../Core/Portable/Workspaces.csproj | 2 +- .../SnapshotSerializationTestBase.cs | 44 +++++------ .../Execution/SnapshotSerializationTests.cs | 12 +-- .../Remote/Core/Services/AssetService.cs | 2 +- .../SnapshotService.JsonRpcAssetSource.cs | 2 +- 22 files changed, 137 insertions(+), 137 deletions(-) rename src/Workspaces/Core/Portable/Execution/{WellKnownSynchronizationKinds.cs => WellKnownSynchronizationKind.cs} (96%) diff --git a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs index 3d9058d48f04c..517a52b0811bb 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs @@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Execution /// internal abstract class CustomAsset : RemotableData { - public CustomAsset(Checksum checksum, WellKnownSynchronizationKinds kind) : base(checksum, kind) + public CustomAsset(Checksum checksum, WellKnownSynchronizationKind kind) : base(checksum, kind) { } } @@ -26,7 +26,7 @@ internal sealed class SimpleCustomAsset : CustomAsset { private readonly Action _writer; - public SimpleCustomAsset(WellKnownSynchronizationKinds kind, Action writer) : + public SimpleCustomAsset(WellKnownSynchronizationKind kind, Action writer) : base(CreateChecksumFromStreamWriter(kind, writer), kind) { // unlike SolutionAsset which gets checksum from solution states, this one build one by itself. @@ -39,7 +39,7 @@ public override Task WriteObjectToAsync(ObjectWriter writer, CancellationToken c return SpecializedTasks.EmptyTask; } - private static Checksum CreateChecksumFromStreamWriter(WellKnownSynchronizationKinds kind, Action writer) + private static Checksum CreateChecksumFromStreamWriter(WellKnownSynchronizationKind kind, Action writer) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) @@ -68,7 +68,7 @@ internal sealed class WorkspaceAnalyzerReferenceAsset : CustomAsset public WorkspaceAnalyzerReferenceAsset(AnalyzerReference reference, Serializer serializer) : base( serializer.CreateChecksum(reference, CancellationToken.None), - WellKnownSynchronizationKinds.AnalyzerReference) + WellKnownSynchronizationKind.AnalyzerReference) { _reference = reference; _serializer = serializer; diff --git a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs index cbf54fc1c0cfc..5322d2e0a8c88 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs @@ -32,7 +32,7 @@ public CustomAsset Build(OptionSet options, string language, CancellationToken c { cancellationToken.ThrowIfCancellationRequested(); - return new SimpleCustomAsset(WellKnownSynchronizationKinds.OptionSet, + return new SimpleCustomAsset(WellKnownSynchronizationKind.OptionSet, (writer, cancellationTokenOnStreamWriting) => _serializer.SerializeOptionSet(options, language, writer, cancellationTokenOnStreamWriting)); } diff --git a/src/Workspaces/Core/Portable/Execution/Extensions.cs b/src/Workspaces/Core/Portable/Execution/Extensions.cs index 481b1d600c9c4..99a1b3c6767ac 100644 --- a/src/Workspaces/Core/Portable/Execution/Extensions.cs +++ b/src/Workspaces/Core/Portable/Execution/Extensions.cs @@ -15,30 +15,30 @@ public static T[] ReadArray(this ObjectReader reader) return (T[])reader.ReadValue(); } - public static WellKnownSynchronizationKinds GetWellKnownSynchronizationKind(this object value) + public static WellKnownSynchronizationKind GetWellKnownSynchronizationKind(this object value) { switch (value) { - case SolutionStateChecksums _: return WellKnownSynchronizationKinds.SolutionState; - case ProjectStateChecksums _: return WellKnownSynchronizationKinds.ProjectState; - case DocumentStateChecksums _: return WellKnownSynchronizationKinds.DocumentState; - case ProjectChecksumCollection _: return WellKnownSynchronizationKinds.Projects; - case DocumentChecksumCollection _: return WellKnownSynchronizationKinds.Documents; - case TextDocumentChecksumCollection _: return WellKnownSynchronizationKinds.TextDocuments; - case ProjectReferenceChecksumCollection _: return WellKnownSynchronizationKinds.ProjectReferences; - case MetadataReferenceChecksumCollection _: return WellKnownSynchronizationKinds.MetadataReferences; - case AnalyzerReferenceChecksumCollection _: return WellKnownSynchronizationKinds.AnalyzerReferences; - case SolutionInfo.SolutionAttributes _: return WellKnownSynchronizationKinds.SolutionAttributes; - case ProjectInfo.ProjectAttributes _: return WellKnownSynchronizationKinds.ProjectAttributes; - case DocumentInfo.DocumentAttributes _: return WellKnownSynchronizationKinds.DocumentAttributes; - case CompilationOptions _: return WellKnownSynchronizationKinds.CompilationOptions; - case ParseOptions _: return WellKnownSynchronizationKinds.ParseOptions; - case ProjectReference _: return WellKnownSynchronizationKinds.ProjectReference; - case MetadataReference _: return WellKnownSynchronizationKinds.MetadataReference; - case AnalyzerReference _: return WellKnownSynchronizationKinds.AnalyzerReference; - case TextDocumentState _: return WellKnownSynchronizationKinds.RecoverableSourceText; - case SourceText _: return WellKnownSynchronizationKinds.SourceText; - case OptionSet _: return WellKnownSynchronizationKinds.OptionSet; + case SolutionStateChecksums _: return WellKnownSynchronizationKind.SolutionState; + case ProjectStateChecksums _: return WellKnownSynchronizationKind.ProjectState; + case DocumentStateChecksums _: return WellKnownSynchronizationKind.DocumentState; + case ProjectChecksumCollection _: return WellKnownSynchronizationKind.Projects; + case DocumentChecksumCollection _: return WellKnownSynchronizationKind.Documents; + case TextDocumentChecksumCollection _: return WellKnownSynchronizationKind.TextDocuments; + case ProjectReferenceChecksumCollection _: return WellKnownSynchronizationKind.ProjectReferences; + case MetadataReferenceChecksumCollection _: return WellKnownSynchronizationKind.MetadataReferences; + case AnalyzerReferenceChecksumCollection _: return WellKnownSynchronizationKind.AnalyzerReferences; + case SolutionInfo.SolutionAttributes _: return WellKnownSynchronizationKind.SolutionAttributes; + case ProjectInfo.ProjectAttributes _: return WellKnownSynchronizationKind.ProjectAttributes; + case DocumentInfo.DocumentAttributes _: return WellKnownSynchronizationKind.DocumentAttributes; + case CompilationOptions _: return WellKnownSynchronizationKind.CompilationOptions; + case ParseOptions _: return WellKnownSynchronizationKind.ParseOptions; + case ProjectReference _: return WellKnownSynchronizationKind.ProjectReference; + case MetadataReference _: return WellKnownSynchronizationKind.MetadataReference; + case AnalyzerReference _: return WellKnownSynchronizationKind.AnalyzerReference; + case TextDocumentState _: return WellKnownSynchronizationKind.RecoverableSourceText; + case SourceText _: return WellKnownSynchronizationKind.SourceText; + case OptionSet _: return WellKnownSynchronizationKind.OptionSet; } throw ExceptionUtilities.UnexpectedValue(value); diff --git a/src/Workspaces/Core/Portable/Execution/RemotableData.cs b/src/Workspaces/Core/Portable/Execution/RemotableData.cs index 94fa61fffc84e..8b35d69adfad7 100644 --- a/src/Workspaces/Core/Portable/Execution/RemotableData.cs +++ b/src/Workspaces/Core/Portable/Execution/RemotableData.cs @@ -16,19 +16,19 @@ internal abstract partial class RemotableData /// /// Indicates what kind of object it is - /// for examples. + /// for examples. /// /// this will be used in tranportation framework and deserialization service /// to hand shake how to send over data and deserialize serialized data /// - public readonly WellKnownSynchronizationKinds Kind; + public readonly WellKnownSynchronizationKind Kind; /// /// Checksum of this object /// public readonly Checksum Checksum; - public RemotableData(Checksum checksum, WellKnownSynchronizationKinds kind) + public RemotableData(Checksum checksum, WellKnownSynchronizationKind kind) { Checksum = checksum; Kind = kind; @@ -47,7 +47,7 @@ public RemotableData(Checksum checksum, WellKnownSynchronizationKinds kind) private sealed class NullRemotableData : RemotableData { public NullRemotableData() : - base(Checksum.Null, WellKnownSynchronizationKinds.Null) + base(Checksum.Null, WellKnownSynchronizationKind.Null) { // null object has null kind and null checksum. // this null object is known to checksum framework and transportation framework to handle null case diff --git a/src/Workspaces/Core/Portable/Execution/Serializer.cs b/src/Workspaces/Core/Portable/Execution/Serializer.cs index e5b0e4ad90fb4..b8a5c382b7bd7 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer.cs @@ -63,21 +63,21 @@ public Checksum CreateChecksum(object value, CancellationToken cancellationToken switch (kind) { - case WellKnownSynchronizationKinds.Null: + case WellKnownSynchronizationKind.Null: return Checksum.Null; - case WellKnownSynchronizationKinds.CompilationOptions: - case WellKnownSynchronizationKinds.ParseOptions: - case WellKnownSynchronizationKinds.ProjectReference: + case WellKnownSynchronizationKind.CompilationOptions: + case WellKnownSynchronizationKind.ParseOptions: + case WellKnownSynchronizationKind.ProjectReference: return Checksum.Create(kind, value, this); - case WellKnownSynchronizationKinds.MetadataReference: + case WellKnownSynchronizationKind.MetadataReference: return Checksum.Create(kind, _hostSerializationService.CreateChecksum((MetadataReference)value, cancellationToken)); - case WellKnownSynchronizationKinds.AnalyzerReference: + case WellKnownSynchronizationKind.AnalyzerReference: return Checksum.Create(kind, _hostSerializationService.CreateChecksum((AnalyzerReference)value, cancellationToken)); - case WellKnownSynchronizationKinds.SourceText: + case WellKnownSynchronizationKind.SourceText: return Checksum.Create(kind, ((SourceText)value).GetChecksum()); default: @@ -104,37 +104,37 @@ public void Serialize(object value, ObjectWriter writer, CancellationToken cance switch (kind) { - case WellKnownSynchronizationKinds.Null: + case WellKnownSynchronizationKind.Null: // do nothing return; - case WellKnownSynchronizationKinds.SolutionAttributes: - case WellKnownSynchronizationKinds.ProjectAttributes: - case WellKnownSynchronizationKinds.DocumentAttributes: + case WellKnownSynchronizationKind.SolutionAttributes: + case WellKnownSynchronizationKind.ProjectAttributes: + case WellKnownSynchronizationKind.DocumentAttributes: ((IObjectWritable)value).WriteTo(writer); return; - case WellKnownSynchronizationKinds.CompilationOptions: + case WellKnownSynchronizationKind.CompilationOptions: SerializeCompilationOptions((CompilationOptions)value, writer, cancellationToken); return; - case WellKnownSynchronizationKinds.ParseOptions: + case WellKnownSynchronizationKind.ParseOptions: SerializeParseOptions((ParseOptions)value, writer, cancellationToken); return; - case WellKnownSynchronizationKinds.ProjectReference: + case WellKnownSynchronizationKind.ProjectReference: SerializeProjectReference((ProjectReference)value, writer, cancellationToken); return; - case WellKnownSynchronizationKinds.MetadataReference: + case WellKnownSynchronizationKind.MetadataReference: SerializeMetadataReference((MetadataReference)value, writer, cancellationToken); return; - case WellKnownSynchronizationKinds.AnalyzerReference: + case WellKnownSynchronizationKind.AnalyzerReference: SerializeAnalyzerReference((AnalyzerReference)value, writer, usePathFromAssembly: true, cancellationToken: cancellationToken); return; - case WellKnownSynchronizationKinds.SourceText: + case WellKnownSynchronizationKind.SourceText: SerializeSourceText(storage: null, text: (SourceText)value, writer: writer, cancellationToken: cancellationToken); return; @@ -146,7 +146,7 @@ public void Serialize(object value, ObjectWriter writer, CancellationToken cance } } - public T Deserialize(WellKnownSynchronizationKinds kind, ObjectReader reader, CancellationToken cancellationToken) + public T Deserialize(WellKnownSynchronizationKind kind, ObjectReader reader, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Serializer_Deserialize, kind.ToString(), cancellationToken)) { @@ -154,39 +154,39 @@ public T Deserialize(WellKnownSynchronizationKinds kind, ObjectReader reader, switch (kind) { - case WellKnownSynchronizationKinds.Null: + case WellKnownSynchronizationKind.Null: return default(T); - case WellKnownSynchronizationKinds.SolutionState: - case WellKnownSynchronizationKinds.ProjectState: - case WellKnownSynchronizationKinds.DocumentState: - case WellKnownSynchronizationKinds.Projects: - case WellKnownSynchronizationKinds.Documents: - case WellKnownSynchronizationKinds.TextDocuments: - case WellKnownSynchronizationKinds.ProjectReferences: - case WellKnownSynchronizationKinds.MetadataReferences: - case WellKnownSynchronizationKinds.AnalyzerReferences: + case WellKnownSynchronizationKind.SolutionState: + case WellKnownSynchronizationKind.ProjectState: + case WellKnownSynchronizationKind.DocumentState: + case WellKnownSynchronizationKind.Projects: + case WellKnownSynchronizationKind.Documents: + case WellKnownSynchronizationKind.TextDocuments: + case WellKnownSynchronizationKind.ProjectReferences: + case WellKnownSynchronizationKind.MetadataReferences: + case WellKnownSynchronizationKind.AnalyzerReferences: return (T)(object)DeserializeChecksumWithChildren(reader, cancellationToken); - case WellKnownSynchronizationKinds.SolutionAttributes: + case WellKnownSynchronizationKind.SolutionAttributes: return (T)(object)SolutionInfo.SolutionAttributes.ReadFrom(reader); - case WellKnownSynchronizationKinds.ProjectAttributes: + case WellKnownSynchronizationKind.ProjectAttributes: return (T)(object)ProjectInfo.ProjectAttributes.ReadFrom(reader); - case WellKnownSynchronizationKinds.DocumentAttributes: + case WellKnownSynchronizationKind.DocumentAttributes: return (T)(object)DocumentInfo.DocumentAttributes.ReadFrom(reader); - case WellKnownSynchronizationKinds.CompilationOptions: + case WellKnownSynchronizationKind.CompilationOptions: return (T)(object)DeserializeCompilationOptions(reader, cancellationToken); - case WellKnownSynchronizationKinds.ParseOptions: + case WellKnownSynchronizationKind.ParseOptions: return (T)(object)DeserializeParseOptions(reader, cancellationToken); - case WellKnownSynchronizationKinds.ProjectReference: + case WellKnownSynchronizationKind.ProjectReference: return (T)(object)DeserializeProjectReference(reader, cancellationToken); - case WellKnownSynchronizationKinds.MetadataReference: + case WellKnownSynchronizationKind.MetadataReference: return (T)(object)DeserializeMetadataReference(reader, cancellationToken); - case WellKnownSynchronizationKinds.AnalyzerReference: + case WellKnownSynchronizationKind.AnalyzerReference: return (T)(object)DeserializeAnalyzerReference(reader, cancellationToken); - case WellKnownSynchronizationKinds.SourceText: + case WellKnownSynchronizationKind.SourceText: return (T)(object)DeserializeSourceText(reader, cancellationToken); - case WellKnownSynchronizationKinds.OptionSet: + case WellKnownSynchronizationKind.OptionSet: return (T)(object)DeserializeOptionSet(reader, cancellationToken); default: diff --git a/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs b/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs index 38f4aafe4dd4b..1d8cb16bbf208 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer_ChecksumWithChildren.cs @@ -17,7 +17,7 @@ internal partial class Serializer private const byte ChecksumKind = 0; private const byte ChecksumWithChildrenKind = 1; - private static readonly ImmutableDictionary> s_creatorMap = CreateCreatorMap(); + private static readonly ImmutableDictionary> s_creatorMap = CreateCreatorMap(); public void SerializeChecksumWithChildren(ChecksumWithChildren checksums, ObjectWriter writer, CancellationToken cancellationToken) { @@ -54,7 +54,7 @@ private ChecksumWithChildren DeserializeChecksumWithChildren(ObjectReader reader { cancellationToken.ThrowIfCancellationRequested(); - var kind = (WellKnownSynchronizationKinds)reader.ReadInt32(); + var kind = (WellKnownSynchronizationKind)reader.ReadInt32(); var checksum = Checksum.ReadFrom(reader); var childrenCount = reader.ReadInt32(); @@ -84,18 +84,18 @@ private ChecksumWithChildren DeserializeChecksumWithChildren(ObjectReader reader return checksums; } - private static ImmutableDictionary> CreateCreatorMap() + private static ImmutableDictionary> CreateCreatorMap() { - return ImmutableDictionary>.Empty - .Add(WellKnownSynchronizationKinds.SolutionState, children => new SolutionStateChecksums(children)) - .Add(WellKnownSynchronizationKinds.ProjectState, children => new ProjectStateChecksums(children)) - .Add(WellKnownSynchronizationKinds.DocumentState, children => new DocumentStateChecksums(children)) - .Add(WellKnownSynchronizationKinds.Projects, children => new ProjectChecksumCollection(children)) - .Add(WellKnownSynchronizationKinds.Documents, children => new DocumentChecksumCollection(children)) - .Add(WellKnownSynchronizationKinds.TextDocuments, children => new TextDocumentChecksumCollection(children)) - .Add(WellKnownSynchronizationKinds.ProjectReferences, children => new ProjectReferenceChecksumCollection(children)) - .Add(WellKnownSynchronizationKinds.MetadataReferences, children => new MetadataReferenceChecksumCollection(children)) - .Add(WellKnownSynchronizationKinds.AnalyzerReferences, children => new AnalyzerReferenceChecksumCollection(children)); + return ImmutableDictionary>.Empty + .Add(WellKnownSynchronizationKind.SolutionState, children => new SolutionStateChecksums(children)) + .Add(WellKnownSynchronizationKind.ProjectState, children => new ProjectStateChecksums(children)) + .Add(WellKnownSynchronizationKind.DocumentState, children => new DocumentStateChecksums(children)) + .Add(WellKnownSynchronizationKind.Projects, children => new ProjectChecksumCollection(children)) + .Add(WellKnownSynchronizationKind.Documents, children => new DocumentChecksumCollection(children)) + .Add(WellKnownSynchronizationKind.TextDocuments, children => new TextDocumentChecksumCollection(children)) + .Add(WellKnownSynchronizationKind.ProjectReferences, children => new ProjectReferenceChecksumCollection(children)) + .Add(WellKnownSynchronizationKind.MetadataReferences, children => new MetadataReferenceChecksumCollection(children)) + .Add(WellKnownSynchronizationKind.AnalyzerReferences, children => new AnalyzerReferenceChecksumCollection(children)); } } } diff --git a/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs b/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs index 124bc1cf7af86..86fbf604e2260 100644 --- a/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/SolutionAsset.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Execution /// internal abstract class SolutionAsset : RemotableData { - protected SolutionAsset(Checksum checksum, WellKnownSynchronizationKinds kind) + protected SolutionAsset(Checksum checksum, WellKnownSynchronizationKind kind) : base(checksum, kind) { } @@ -64,7 +64,7 @@ internal sealed class SourceTextAsset : SolutionAsset private readonly Serializer _serializer; public SourceTextAsset(Checksum checksum, TextDocumentState state, Serializer serializer) : - base(checksum, WellKnownSynchronizationKinds.SourceText) + base(checksum, WellKnownSynchronizationKind.SourceText) { _state = state; _serializer = serializer; diff --git a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs similarity index 96% rename from src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs rename to src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs index 066d0b55ca22a..a939b61b0781f 100644 --- a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKinds.cs +++ b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs @@ -3,7 +3,7 @@ namespace Microsoft.CodeAnalysis.Serialization { // TODO: Kind might not actually needed. see whether we can get rid of this - internal enum WellKnownSynchronizationKinds + internal enum WellKnownSynchronizationKind { Null, diff --git a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs index a5d80a8a135a5..ab4876e509b89 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SymbolTree/SymbolTreeInfo_Source.cs @@ -66,7 +66,7 @@ public static async Task GetSourceSymbolsChecksumAsync(Project project allChecksums.Add(compilationOptionsChecksum); allChecksums.Add(parseOptionsChecksum); - var checksum = Checksum.Create(WellKnownSynchronizationKinds.SymbolTreeInfo, allChecksums); + var checksum = Checksum.Create(WellKnownSynchronizationKind.SymbolTreeInfo, allChecksums); return checksum; } finally diff --git a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs index a0a6a0d585901..42f0c9a6bf9ba 100644 --- a/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs +++ b/src/Workspaces/Core/Portable/FindSymbols/SyntaxTree/SyntaxTreeIndex_Persistence.cs @@ -89,7 +89,7 @@ public static async Task GetChecksumAsync( var parseOptionsChecksum = ChecksumCache.GetOrCreate( parseOptions, _ => serializer.CreateChecksum(parseOptions, cancellationToken)); - return Checksum.Create(WellKnownSynchronizationKinds.SyntaxTreeIndex, new[] { textChecksum, parseOptionsChecksum }); + return Checksum.Create(WellKnownSynchronizationKind.SyntaxTreeIndex, new[] { textChecksum, parseOptionsChecksum }); } private async Task SaveAsync( diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs index 1d5a72da8fab4..008ecaeebdf3a 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumCollection.cs @@ -12,11 +12,11 @@ namespace Microsoft.CodeAnalysis.Serialization /// internal abstract class ChecksumCollection : ChecksumWithChildren, IEnumerable { - protected ChecksumCollection(WellKnownSynchronizationKinds kind, Checksum[] checksums) : this(kind, (object[])checksums) + protected ChecksumCollection(WellKnownSynchronizationKind kind, Checksum[] checksums) : this(kind, (object[])checksums) { } - protected ChecksumCollection(WellKnownSynchronizationKinds kind, object[] checksums) : base(kind, checksums) + protected ChecksumCollection(WellKnownSynchronizationKind kind, object[] checksums) : base(kind, checksums) { } @@ -38,36 +38,36 @@ IEnumerator IEnumerable.GetEnumerator() internal class ProjectChecksumCollection : ChecksumCollection { public ProjectChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public ProjectChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.ProjectChecksumCollection, checksums) { } + public ProjectChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.ProjectChecksumCollection, checksums) { } } internal class DocumentChecksumCollection : ChecksumCollection { public DocumentChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public DocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.DocumentChecksumCollection, checksums) { } + public DocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.DocumentChecksumCollection, checksums) { } } internal class TextDocumentChecksumCollection : ChecksumCollection { public TextDocumentChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public TextDocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.TextDocumentChecksumCollection, checksums) { } + public TextDocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.TextDocumentChecksumCollection, checksums) { } } internal class ProjectReferenceChecksumCollection : ChecksumCollection { public ProjectReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public ProjectReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.ProjectReferenceChecksumCollection, checksums) { } + public ProjectReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.ProjectReferenceChecksumCollection, checksums) { } } internal class MetadataReferenceChecksumCollection : ChecksumCollection { public MetadataReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public MetadataReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.MetadataReferenceChecksumCollection, checksums) { } + public MetadataReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.MetadataReferenceChecksumCollection, checksums) { } } internal class AnalyzerReferenceChecksumCollection : ChecksumCollection { public AnalyzerReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { } - public AnalyzerReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKinds.AnalyzerReferenceChecksumCollection, checksums) { } + public AnalyzerReferenceChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.AnalyzerReferenceChecksumCollection, checksums) { } } } diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs index 43461c80b0d76..8a96e582ed5ac 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ChecksumWithChildren.cs @@ -10,7 +10,7 @@ namespace Microsoft.CodeAnalysis.Serialization /// internal abstract class ChecksumWithChildren : IChecksummedObject { - public ChecksumWithChildren(WellKnownSynchronizationKinds kind, params object[] children) + public ChecksumWithChildren(WellKnownSynchronizationKind kind, params object[] children) { Checksum = CreateChecksum(kind, children); Children = children; @@ -20,7 +20,7 @@ public ChecksumWithChildren(WellKnownSynchronizationKinds kind, params object[] public IReadOnlyList Children { get; } - private static Checksum CreateChecksum(WellKnownSynchronizationKinds kind, object[] children) + private static Checksum CreateChecksum(WellKnownSynchronizationKind kind, object[] children) { // given children must be either Checksum or Checksums (collection of a checksum) return Checksum.Create(kind, children.Select(c => c as Checksum ?? ((ChecksumCollection)c).Checksum)); diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index dbc50f77793f8..cc95c9d326bea 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -24,7 +24,7 @@ public static Checksum Create(Stream stream) } } - public static Checksum Create(WellKnownSynchronizationKinds kind, IObjectWritable @object) + public static Checksum Create(WellKnownSynchronizationKind kind, IObjectWritable @object) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) @@ -36,7 +36,7 @@ public static Checksum Create(WellKnownSynchronizationKinds kind, IObjectWritabl } } - public static Checksum Create(WellKnownSynchronizationKinds kind, IEnumerable checksums) + public static Checksum Create(WellKnownSynchronizationKind kind, IEnumerable checksums) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream)) @@ -52,7 +52,7 @@ public static Checksum Create(WellKnownSynchronizationKinds kind, IEnumerable bytes) + public static Checksum Create(WellKnownSynchronizationKind kind, ImmutableArray bytes) { using (var stream = SerializableBytes.CreateWritableStream()) using (var writer = new ObjectWriter(stream)) @@ -68,7 +68,7 @@ public static Checksum Create(WellKnownSynchronizationKinds kind, ImmutableArray } } - public static Checksum Create(WellKnownSynchronizationKinds kind, T value, Serializer serializer) + public static Checksum Create(WellKnownSynchronizationKind kind, T value, Serializer serializer) { using (var stream = SerializableBytes.CreateWritableStream()) using (var objectWriter = new ObjectWriter(stream)) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs index 0ea5dc98aa411..a6ac6503361c3 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/DocumentInfo.cs @@ -235,7 +235,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.DocumentAttributes, this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKind.DocumentAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs index b0f34d640040e..f40bcdd9de367 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/ProjectInfo.cs @@ -475,7 +475,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.ProjectAttributes, this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKind.ProjectAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs index f5d9029c9ad7c..a76b498de81c8 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SolutionInfo.cs @@ -139,7 +139,7 @@ Checksum IChecksummedObject.Checksum { if (_lazyChecksum == null) { - _lazyChecksum = Checksum.Create(WellKnownSynchronizationKinds.SolutionAttributes, this); + _lazyChecksum = Checksum.Create(WellKnownSynchronizationKind.SolutionAttributes, this); } return _lazyChecksum; diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs index bc873f7ff2f64..bef86d7f5264d 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/StateChecksums.cs @@ -16,7 +16,7 @@ public SolutionStateChecksums(Checksum infoChecksum, ProjectChecksumCollection p { } - public SolutionStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.SolutionStateChecksums, children) + public SolutionStateChecksums(params object[] children) : base(WellKnownSynchronizationKind.SolutionStateChecksums, children) { } @@ -94,7 +94,7 @@ public ProjectStateChecksums( { } - public ProjectStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.ProjectStateChecksums, children) + public ProjectStateChecksums(params object[] children) : base(WellKnownSynchronizationKind.ProjectStateChecksums, children) { } @@ -228,7 +228,7 @@ public DocumentStateChecksums(Checksum infoChecksum, Checksum textChecksum) : { } - public DocumentStateChecksums(params object[] children) : base(WellKnownSynchronizationKinds.DocumentStateChecksums, children) + public DocumentStateChecksums(params object[] children) : base(WellKnownSynchronizationKind.DocumentStateChecksums, children) { } diff --git a/src/Workspaces/Core/Portable/Workspaces.csproj b/src/Workspaces/Core/Portable/Workspaces.csproj index 74ab71906e6bf..2b343008c486d 100644 --- a/src/Workspaces/Core/Portable/Workspaces.csproj +++ b/src/Workspaces/Core/Portable/Workspaces.csproj @@ -368,7 +368,7 @@ - + diff --git a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs index 69a44e59f0e45..832430e20f6ef 100644 --- a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs +++ b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTestBase.cs @@ -39,7 +39,7 @@ internal static Solution CreateFullSolution(HostServices hostServices = null) internal static async Task VerifyAssetAsync(ISolutionSynchronizationService service, SolutionStateChecksums solutionObject) { await VerifyAssetSerializationAsync( - service, solutionObject.Info, WellKnownSynchronizationKinds.SolutionAttributes, + service, solutionObject.Info, WellKnownSynchronizationKind.SolutionAttributes, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)).ConfigureAwait(false); foreach (var projectChecksum in solutionObject.Projects) @@ -52,15 +52,15 @@ internal static async Task VerifyAssetAsync(ISolutionSynchronizationService serv internal static async Task VerifyAssetAsync(ISolutionSynchronizationService service, ProjectStateChecksums projectObject) { var info = await VerifyAssetSerializationAsync( - service, projectObject.Info, WellKnownSynchronizationKinds.ProjectAttributes, + service, projectObject.Info, WellKnownSynchronizationKind.ProjectAttributes, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)).ConfigureAwait(false); await VerifyAssetSerializationAsync( - service, projectObject.CompilationOptions, WellKnownSynchronizationKinds.CompilationOptions, + service, projectObject.CompilationOptions, WellKnownSynchronizationKind.CompilationOptions, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); await VerifyAssetSerializationAsync( - service, projectObject.ParseOptions, WellKnownSynchronizationKinds.ParseOptions, + service, projectObject.ParseOptions, WellKnownSynchronizationKind.ParseOptions, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); foreach (var checksum in projectObject.Documents) @@ -72,21 +72,21 @@ await VerifyAssetSerializationAsync( foreach (var checksum in projectObject.ProjectReferences) { await VerifyAssetSerializationAsync( - service, checksum, WellKnownSynchronizationKinds.ProjectReference, + service, checksum, WellKnownSynchronizationKind.ProjectReference, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); } foreach (var checksum in projectObject.MetadataReferences) { await VerifyAssetSerializationAsync( - service, checksum, WellKnownSynchronizationKinds.MetadataReference, + service, checksum, WellKnownSynchronizationKind.MetadataReference, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); } foreach (var checksum in projectObject.AnalyzerReferences) { await VerifyAssetSerializationAsync( - service, checksum, WellKnownSynchronizationKinds.AnalyzerReference, + service, checksum, WellKnownSynchronizationKind.AnalyzerReference, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); } @@ -100,19 +100,19 @@ await VerifyAssetSerializationAsync( internal static async Task VerifyAssetAsync(ISolutionSynchronizationService service, DocumentStateChecksums documentObject) { var info = await VerifyAssetSerializationAsync( - service, documentObject.Info, WellKnownSynchronizationKinds.DocumentAttributes, + service, documentObject.Info, WellKnownSynchronizationKind.DocumentAttributes, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)).ConfigureAwait(false); await VerifyAssetSerializationAsync( - service, documentObject.Text, WellKnownSynchronizationKinds.SourceText, + service, documentObject.Text, WellKnownSynchronizationKind.SourceText, (v, k, s) => SolutionAsset.Create(s.CreateChecksum(v, CancellationToken.None), v, s)); } internal static async Task VerifyAssetSerializationAsync( ISolutionSynchronizationService service, Checksum checksum, - WellKnownSynchronizationKinds kind, - Func assetGetter) + WellKnownSynchronizationKind kind, + Func assetGetter) { // re-create asset from object var syncService = (SolutionSynchronizationServiceFactory.Service)service; @@ -206,20 +206,20 @@ internal static void VerifySnapshotInService( int expectedAdditionalDocumentCount) { VerifyChecksumInService(snapshotService, projectObject.Checksum, projectObject.GetWellKnownSynchronizationKind()); - VerifyChecksumInService(snapshotService, projectObject.Info, WellKnownSynchronizationKinds.ProjectAttributes); - VerifyChecksumInService(snapshotService, projectObject.CompilationOptions, WellKnownSynchronizationKinds.CompilationOptions); - VerifyChecksumInService(snapshotService, projectObject.ParseOptions, WellKnownSynchronizationKinds.ParseOptions); + VerifyChecksumInService(snapshotService, projectObject.Info, WellKnownSynchronizationKind.ProjectAttributes); + VerifyChecksumInService(snapshotService, projectObject.CompilationOptions, WellKnownSynchronizationKind.CompilationOptions); + VerifyChecksumInService(snapshotService, projectObject.ParseOptions, WellKnownSynchronizationKind.ParseOptions); VerifyCollectionInService(snapshotService, projectObject.Documents.ToDocumentObjects(snapshotService), expectedDocumentCount); - VerifyCollectionInService(snapshotService, projectObject.ProjectReferences, expectedProjectReferenceCount, WellKnownSynchronizationKinds.ProjectReference); - VerifyCollectionInService(snapshotService, projectObject.MetadataReferences, expectedMetadataReferenceCount, WellKnownSynchronizationKinds.MetadataReference); - VerifyCollectionInService(snapshotService, projectObject.AnalyzerReferences, expectedAnalyzerReferenceCount, WellKnownSynchronizationKinds.AnalyzerReference); + VerifyCollectionInService(snapshotService, projectObject.ProjectReferences, expectedProjectReferenceCount, WellKnownSynchronizationKind.ProjectReference); + VerifyCollectionInService(snapshotService, projectObject.MetadataReferences, expectedMetadataReferenceCount, WellKnownSynchronizationKind.MetadataReference); + VerifyCollectionInService(snapshotService, projectObject.AnalyzerReferences, expectedAnalyzerReferenceCount, WellKnownSynchronizationKind.AnalyzerReference); VerifyCollectionInService(snapshotService, projectObject.AdditionalDocuments.ToDocumentObjects(snapshotService), expectedAdditionalDocumentCount); } - internal static void VerifyCollectionInService(ISolutionSynchronizationService snapshotService, ChecksumCollection checksums, int expectedCount, WellKnownSynchronizationKinds expectedItemKind) + internal static void VerifyCollectionInService(ISolutionSynchronizationService snapshotService, ChecksumCollection checksums, int expectedCount, WellKnownSynchronizationKind expectedItemKind) { VerifyChecksumInService(snapshotService, checksums.Checksum, checksums.GetWellKnownSynchronizationKind()); Assert.Equal(checksums.Count, expectedCount); @@ -244,8 +244,8 @@ internal static void VerifyCollectionInService(ISolutionSynchronizationService s internal static void VerifySnapshotInService(ISolutionSynchronizationService snapshotService, DocumentStateChecksums documentObject) { VerifyChecksumInService(snapshotService, documentObject.Checksum, documentObject.GetWellKnownSynchronizationKind()); - VerifyChecksumInService(snapshotService, documentObject.Info, WellKnownSynchronizationKinds.DocumentAttributes); - VerifyChecksumInService(snapshotService, documentObject.Text, WellKnownSynchronizationKinds.SourceText); + VerifyChecksumInService(snapshotService, documentObject.Info, WellKnownSynchronizationKind.DocumentAttributes); + VerifyChecksumInService(snapshotService, documentObject.Text, WellKnownSynchronizationKind.SourceText); } internal static void VerifySynchronizationObjectInService(ISolutionSynchronizationService snapshotService, T syncObject) where T : RemotableData @@ -253,7 +253,7 @@ internal static void VerifySynchronizationObjectInService(ISolutionSynchroniz VerifyChecksumInService(snapshotService, syncObject.Checksum, syncObject.Kind); } - internal static void VerifyChecksumInService(ISolutionSynchronizationService snapshotService, Checksum checksum, WellKnownSynchronizationKinds kind) + internal static void VerifyChecksumInService(ISolutionSynchronizationService snapshotService, Checksum checksum, WellKnownSynchronizationKind kind) { Assert.NotNull(checksum); var otherObject = snapshotService.GetRemotableData(checksum, CancellationToken.None); @@ -266,7 +266,7 @@ internal static void SynchronizationObjectEqual(T checksumObject1, T checksum ChecksumEqual(checksumObject1.Checksum, checksumObject1.Kind, checksumObject2.Checksum, checksumObject2.Kind); } - internal static void ChecksumEqual(Checksum checksum1, WellKnownSynchronizationKinds kind1, Checksum checksum2, WellKnownSynchronizationKinds kind2) + internal static void ChecksumEqual(Checksum checksum1, WellKnownSynchronizationKind kind1, Checksum checksum2, WellKnownSynchronizationKind kind2) { Assert.Equal(checksum1, checksum2); Assert.Equal(kind1, kind2); diff --git a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs index adacfe3682c93..152c06e12caae 100644 --- a/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs +++ b/src/Workspaces/CoreTest/Execution/SnapshotSerializationTests.cs @@ -42,7 +42,7 @@ public async Task CreateSolutionSnapshotId_Empty() VerifySynchronizationObjectInService(snapshotService, solutionSyncObject); var solutionObject = await snapshotService.GetValueAsync(checksum).ConfigureAwait(false); - VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKinds.SolutionAttributes); + VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKind.SolutionAttributes); var projectsSyncObject = snapshotService.GetRemotableData(solutionObject.Projects.Checksum, CancellationToken.None); VerifySynchronizationObjectInService(snapshotService, projectsSyncObject); @@ -78,7 +78,7 @@ public async Task CreateSolutionSnapshotId_Project() var solutionObject = await snapshotService.GetValueAsync(checksum).ConfigureAwait(false); - VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKinds.SolutionAttributes); + VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKind.SolutionAttributes); var projectSyncObject = snapshotService.GetRemotableData(solutionObject.Projects.Checksum, CancellationToken.None); VerifySynchronizationObjectInService(snapshotService, projectSyncObject); @@ -114,8 +114,8 @@ public async Task CreateSolutionSnapshotId() var solutionObject = await snapshotService.GetValueAsync(syncObject.Checksum).ConfigureAwait(false); VerifySynchronizationObjectInService(snapshotService, syncObject); - VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKinds.SolutionAttributes); - VerifyChecksumInService(snapshotService, solutionObject.Projects.Checksum, WellKnownSynchronizationKinds.Projects); + VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKind.SolutionAttributes); + VerifyChecksumInService(snapshotService, solutionObject.Projects.Checksum, WellKnownSynchronizationKind.Projects); Assert.Equal(solutionObject.Projects.Count, 1); VerifySnapshotInService(snapshotService, solutionObject.Projects.ToProjectObjects(snapshotService)[0], 1, 0, 0, 0, 0); @@ -151,8 +151,8 @@ public async Task CreateSolutionSnapshotId_Full() var solutionObject = await snapshotService.GetValueAsync(syncObject.Checksum).ConfigureAwait(false); VerifySynchronizationObjectInService(snapshotService, syncObject); - VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKinds.SolutionAttributes); - VerifyChecksumInService(snapshotService, solutionObject.Projects.Checksum, WellKnownSynchronizationKinds.Projects); + VerifyChecksumInService(snapshotService, solutionObject.Info, WellKnownSynchronizationKind.SolutionAttributes); + VerifyChecksumInService(snapshotService, solutionObject.Projects.Checksum, WellKnownSynchronizationKind.Projects); Assert.Equal(solutionObject.Projects.Count, 2); diff --git a/src/Workspaces/Remote/Core/Services/AssetService.cs b/src/Workspaces/Remote/Core/Services/AssetService.cs index fdb46cce105d4..05caaad9879b9 100644 --- a/src/Workspaces/Remote/Core/Services/AssetService.cs +++ b/src/Workspaces/Remote/Core/Services/AssetService.cs @@ -29,7 +29,7 @@ public AssetService(int sessionId, AssetStorage assetStorage) _assetStorage = assetStorage; } - public T Deserialize(WellKnownSynchronizationKinds kind, ObjectReader reader, CancellationToken cancellationToken) + public T Deserialize(WellKnownSynchronizationKind kind, ObjectReader reader, CancellationToken cancellationToken) { return s_serializer.Deserialize(kind, reader, cancellationToken); } diff --git a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs index 391369afa7c0a..0ae446a02b51b 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/SnapshotService.JsonRpcAssetSource.cs @@ -73,7 +73,7 @@ private IList> ReadAssets( var responseChecksum = Checksum.ReadFrom(reader); Contract.ThrowIfFalse(checksums.Contains(responseChecksum)); - var kind = (WellKnownSynchronizationKinds)reader.ReadInt32(); + var kind = (WellKnownSynchronizationKind)reader.ReadInt32(); // in service hub, cancellation means simply closed stream var @object = _owner.RoslynServices.AssetService.Deserialize(kind, reader, cancellationToken); From 5e8c4977a6672cef7eef0dd3e20a5f47ecf07a7f Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 12:09:41 -0700 Subject: [PATCH 210/214] Add fast path for ToString for this enum. --- .../Core/Portable/Execution/Serializer.cs | 2 +- .../Execution/WellKnownSynchronizationKind.cs | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/Workspaces/Core/Portable/Execution/Serializer.cs b/src/Workspaces/Core/Portable/Execution/Serializer.cs index b8a5c382b7bd7..7a4f5cb2d13df 100644 --- a/src/Workspaces/Core/Portable/Execution/Serializer.cs +++ b/src/Workspaces/Core/Portable/Execution/Serializer.cs @@ -52,7 +52,7 @@ public Checksum CreateChecksum(object value, CancellationToken cancellationToken { var kind = value.GetWellKnownSynchronizationKind(); - using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, kind.ToString(), cancellationToken)) + using (Logger.LogBlock(FunctionId.Serializer_CreateChecksum, kind.ToStringFast(), cancellationToken)) { cancellationToken.ThrowIfCancellationRequested(); diff --git a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs index a939b61b0781f..d6cb9e912d918 100644 --- a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs +++ b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs @@ -1,5 +1,8 @@ // 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.Linq; +using System.Reflection; + namespace Microsoft.CodeAnalysis.Serialization { // TODO: Kind might not actually needed. see whether we can get rid of this @@ -47,4 +50,35 @@ internal enum WellKnownSynchronizationKind ProjectStateChecksums, DocumentStateChecksums, } + + internal static class WellKnownSynchronizationKindExtensions + { + private static readonly string[] s_strings; + + static WellKnownSynchronizationKindExtensions() + { + var fields = typeof(WellKnownSynchronizationKind).GetTypeInfo().DeclaredFields.Where(f => f.IsStatic); + + var maxValue = 0; + foreach (var field in fields) + { + var value = (int)field.GetValue(null); + if (value > maxValue) + { + maxValue = value; + } + } + + s_strings = new string[maxValue + 1]; + + foreach (var field in fields) + { + var value = (int)field.GetValue(null); + s_strings[value] = field.Name; + } + } + + public static string ToStringFast(this WellKnownSynchronizationKind kind) + => s_strings[(int)kind]; + } } \ No newline at end of file From c9f2fe8635251758ceaf0eff638b20fad38a2318 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 12:10:58 -0700 Subject: [PATCH 211/214] Simplify code. --- .../Execution/WellKnownSynchronizationKind.cs | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs index d6cb9e912d918..78bf14f17ed2b 100644 --- a/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs +++ b/src/Workspaces/Core/Portable/Execution/WellKnownSynchronizationKind.cs @@ -59,16 +59,7 @@ static WellKnownSynchronizationKindExtensions() { var fields = typeof(WellKnownSynchronizationKind).GetTypeInfo().DeclaredFields.Where(f => f.IsStatic); - var maxValue = 0; - foreach (var field in fields) - { - var value = (int)field.GetValue(null); - if (value > maxValue) - { - maxValue = value; - } - } - + var maxValue = fields.Max(f => (int)f.GetValue(null)); s_strings = new string[maxValue + 1]; foreach (var field in fields) From 05d8ee3fc435844cf8c4f1e1ed7b40f1dee9cd96 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Mon, 8 May 2017 12:23:32 -0700 Subject: [PATCH 212/214] Remove pooling. --- .../Workspace/Solution/Checksum_Factory.cs | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs index cf95cbab45a41..ed875da803ec9 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/Checksum_Factory.cs @@ -14,27 +14,12 @@ namespace Microsoft.CodeAnalysis // all these are just helper methods internal partial class Checksum { - private static readonly Stack s_hashStack = new Stack(); - private static readonly object s_gate = new object(); - public static Checksum Create(Stream stream) { - IncrementalHash hash; - lock (s_gate) - { - hash = s_hashStack.Count > 0 - ? s_hashStack.Pop() - : IncrementalHash.CreateHash(HashAlgorithmName.SHA1); - } - - var checksum = ComputeChecksum(stream, hash); - - lock (s_gate) + using (var hash = IncrementalHash.CreateHash(HashAlgorithmName.SHA1)) { - s_hashStack.Push(hash); + return ComputeChecksum(stream, hash); } - - return checksum; } private static Checksum ComputeChecksum(Stream stream, IncrementalHash hash) From 79b2bc6e4b99a302605a5cba7e0abb6f9a005726 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Mon, 8 May 2017 13:41:50 -0700 Subject: [PATCH 213/214] Fix crash with typeless tuple in "as" operator (#18416) --- .../Portable/Binder/Binder_Conversions.cs | 6 +- .../Portable/Binder/Binder_Operators.cs | 14 +- .../Portable/CSharpResources.Designer.cs | 9 + .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Test/Emit/CodeGen/CodeGenTupleTest.cs | 200 +++++++++- .../Test/Emit/CodeGen/CodeGenTuples.vb | 359 ++++++++++++++++++ .../InlineTemporary/InlineTemporaryTests.vb | 23 ++ 8 files changed, 602 insertions(+), 13 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index eadedbb274034..42648bd0353c2 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -94,7 +94,7 @@ protected BoundExpression CreateConversion( } if (conversion.IsTupleLiteralConversion || - (conversion.Kind == ConversionKind.ImplicitNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) + (conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) { return CreateTupleLiteralConversion(syntax, (BoundTupleLiteral)source, conversion, isCast, destination, diagnostics); } @@ -343,12 +343,12 @@ private BoundExpression CreateTupleLiteralConversion(SyntaxNode syntax, BoundTup { // We have a successful tuple conversion; rather than producing a separate conversion node // which is a conversion on top of a tuple literal, tuple conversion is an element-wise conversion of arguments. - Debug.Assert((conversion.Kind == ConversionKind.ImplicitNullable) == destination.IsNullableType()); + Debug.Assert(conversion.IsNullable == destination.IsNullableType()); var destinationWithoutNullable = destination; var conversionWithoutNullable = conversion; - if (conversion.Kind == ConversionKind.ImplicitNullable) + if (conversion.IsNullable) { destinationWithoutNullable = destination.GetNullableUnderlyingType(); conversionWithoutNullable = conversion.UnderlyingConversions[0]; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs index c783ac88ae433..d50de79910347 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Operators.cs @@ -3009,6 +3009,14 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa } return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); + + case BoundKind.TupleLiteral: + if ((object)operand.Type == null) + { + Error(diagnostics, ErrorCode.ERR_TypelessTupleInAs, node); + return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); + } + break; } if (operand.HasAnyErrors || targetTypeKind == TypeKind.Error) @@ -3073,12 +3081,6 @@ private BoundExpression BindAsOperator(BinaryExpressionSyntax node, DiagnosticBa type: GetSpecialType(SpecialType.System_Object, diagnostics, node)); } - if (operand.Kind == BoundKind.MethodGroup) - { - Error(diagnostics, ErrorCode.ERR_NoExplicitBuiltinConv, node, MessageID.IDS_MethodGroup.Localize(), targetType); - return new BoundAsOperator(node, operand, typeExpression, Conversion.NoConversion, resultType, hasErrors: true); - } - var operandType = operand.Type; Debug.Assert((object)operandType != null); var operandTypeKind = operandType.TypeKind; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs index 7a1975a98f5f2..65d87f9cc8232 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs +++ b/src/Compilers/CSharp/Portable/CSharpResources.Designer.cs @@ -9151,6 +9151,15 @@ internal static string ERR_TypeInferenceFailedForImplicitlyTypedOutVariable { } } + /// + /// Looks up a localized string similar to The first operand of an 'as' operator may not be a tuple literal without a natural type.. + /// + internal static string ERR_TypelessTupleInAs { + get { + return ResourceManager.GetString("ERR_TypelessTupleInAs", resourceCulture); + } + } + /// /// Looks up a localized string similar to Type parameter declaration must be an identifier not a type. /// diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 9227afe9d86e8..13a64ea0367ed 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -2165,6 +2165,9 @@ If such a class is used as a base class and if the deriving class defines a dest The first operand of an 'is' or 'as' operator may not be a lambda expression, anonymous method, or method group. + + The first operand of an 'as' operator may not be a tuple literal without a natural type. + An expression tree may not contain a multidimensional array initializer diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 40cbe2de7be99..6c6cbeb3ee268 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -1475,6 +1475,7 @@ internal enum ErrorCode ERR_CompilerAndLanguageVersion = 8304, WRN_Experimental = 8305, ERR_TupleInferredNamesNotAvailable = 8306, + ERR_TypelessTupleInAs = 8307, #region diagnostics for C# 7.1 diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs index 2032304befd1c..4448de074facf 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenTupleTest.cs @@ -6605,6 +6605,176 @@ static void Main() ); } + [Fact] + public void TupleExplicitNullableConversionWithTypelessTuple() + { + var source = @" +class C +{ + static void Main() + { + var x = ((int, string)?) (1, null); + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(1, )"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var declaration = tree.GetRoot().DescendantNodes().OfType().First(); + var value = declaration.Declaration.Variables.First().Initializer.Value; + Assert.Equal("((int, string)?) (1, null)", value.ToString()); + var castConversion = model.GetConversion(value); + Assert.Equal(ConversionKind.Identity, castConversion.Kind); + var tuple = ((CastExpressionSyntax)value).Expression; + Assert.Equal("(1, null)", tuple.ToString()); + var tupleConversion = model.GetConversion(tuple); + Assert.Equal(ConversionKind.ExplicitNullable, tupleConversion.Kind); + Assert.Equal(1, tupleConversion.UnderlyingConversions.Length); + Assert.Equal(ConversionKind.ExplicitTupleLiteral, tupleConversion.UnderlyingConversions[0].Kind); + } + + [Fact] + public void TupleImplicitNullableConversionWithTypelessTuple() + { + var source = @" +class C +{ + static void Main() + { + (int, string)? x = (1, null); + System.Console.Write(x); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "(1, )"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + var declaration = tree.GetRoot().DescendantNodes().OfType().First(); + var value = declaration.Declaration.Variables.First().Initializer.Value; + Assert.Equal("(1, null)", value.ToString()); + var tupleConversion = model.GetConversion(value); + Assert.Equal(ConversionKind.ImplicitNullable, tupleConversion.Kind); + Assert.Equal(1, tupleConversion.UnderlyingConversions.Length); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, tupleConversion.UnderlyingConversions[0].Kind); + } + + [Fact] + public void TupleImplicitNullableAndCustomConversionsWithTypelessTuple() + { + var source = @" +struct C +{ + static void Main() + { + C? x = (1, null); + System.Console.Write(x); + var x2 = (C)(2, null); + System.Console.Write(x2); + var x3 = (C?)(3, null); + System.Console.Write(x3); + } + public static implicit operator C((int, string) x) + { + return new C(); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics(); + CompileAndVerify(comp, expectedOutput: "CCC"); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tuples = tree.GetRoot().DescendantNodes().OfType(); + + var tuple1 = tuples.ElementAt(0); + Assert.Equal("(1, null)", tuple1.ToString()); + Assert.Null(model.GetTypeInfo(tuple1).Type); + Assert.Equal("C?", model.GetTypeInfo(tuple1).ConvertedType.ToTestDisplayString()); + + var conversion1 = model.GetConversion(tuple1); + Assert.Equal(ConversionKind.ImplicitUserDefined, conversion1.Kind); + Assert.True(conversion1.UnderlyingConversions.IsDefault); + + var tuple2 = tuples.ElementAt(1); + Assert.Equal("(2, null)", tuple2.ToString()); + Assert.Null(model.GetTypeInfo(tuple2).Type); + Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple2).ConvertedType.ToTestDisplayString()); + + var conversion2 = model.GetConversion(tuple2); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion2.Kind); + Assert.False(conversion2.UnderlyingConversions.IsDefault); + + var tuple3 = tuples.ElementAt(2); + Assert.Equal("(3, null)", tuple3.ToString()); + Assert.Null(model.GetTypeInfo(tuple3).Type); + Assert.Equal("(System.Int32, System.String)", model.GetTypeInfo(tuple3).ConvertedType.ToTestDisplayString()); + + var conversion3 = model.GetConversion(tuple3); + Assert.Equal(ConversionKind.ImplicitTupleLiteral, conversion3.Kind); + Assert.False(conversion3.UnderlyingConversions.IsDefault); + } + + [Fact] + public void TupleImplicitNullableAndCustomConversionsWithTypelessTupleInAsOperator() + { + var source = @" +struct C +{ + static void Main() + { + var x4 = (1, null) as C; + System.Console.Write(x4); + var x5 = (2, null) as C?; + System.Console.Write(x5); + } + public static implicit operator C((int, string) x) + { + return new C(); + } +} +"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }, options: TestOptions.DebugExe); + comp.VerifyEmitDiagnostics( + // (6,18): error CS8305: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x4 = (1, null) as C; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(1, null) as C").WithLocation(6, 18), + // (8,18): error CS8305: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x5 = (2, null) as C?; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(2, null) as C?").WithLocation(8, 18) + ); + + var tree = comp.SyntaxTrees.Single(); + var model = comp.GetSemanticModel(tree); + + var tuples = tree.GetRoot().DescendantNodes().OfType(); + verifyTuple("(1, null)", tuples.ElementAt(0)); + verifyTuple("(2, null)", tuples.ElementAt(1)); + + void verifyTuple(string expected, TupleExpressionSyntax tuple) + { + Assert.Equal(expected, tuple.ToString()); + Assert.Null(model.GetTypeInfo(tuple).Type); + Assert.Null(model.GetTypeInfo(tuple).ConvertedType); + + var conversion = model.GetConversion(tuple); + Assert.Equal(ConversionKind.Identity, conversion.Kind); + Assert.True(conversion.UnderlyingConversions.IsDefault); + } + } + [Fact] public void TupleTargetTypeLambda() { @@ -7069,8 +7239,8 @@ static void Main() Assert.Equal(@"(e: 1, f: ""hello"")", node.ToString()); Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).Type.ToTestDisplayString()); - Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.Identity, model.GetConversion(node)); + Assert.Equal("(System.Int16 c, System.String d)?", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + Assert.Equal(ConversionKind.ExplicitNullable, model.GetConversion(node).Kind); // semantic model returns topmost conversion from the sequence of conversions for // ((short c, string d)?)(e: 1, f: ""hello"") @@ -7295,8 +7465,8 @@ static void Main() Assert.Equal(@"(e: 1, f: ""hello"")", node.ToString()); Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).Type.ToTestDisplayString()); - Assert.Equal("(System.Int32 e, System.String f)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); - Assert.Equal(Conversion.Identity, model.GetConversion(node)); + Assert.Equal("(System.Int32 c, System.String d)?", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()); + Assert.Equal(ConversionKind.ExplicitNullable, model.GetConversion(node).Kind); // semantic model returns topmost conversion from the sequence of conversions for // ((int c, string d)?)(e: 1, f: ""hello"") @@ -22680,5 +22850,27 @@ public struct ValueTuple Assert.False(tuple3.IsErrorType()); Assert.Equal(libWithVTRef.Display, tuple3.ContainingAssembly.MetadataName.ToString()); } + + [Fact] + [WorkItem(17962, "https://github.com/dotnet/roslyn/issues/17962")] + public void TupleWithAsOperator() + { + var source = @" +class C +{ + void M() + { + var x = (0, null) as (int, T)?; + System.Console.WriteLine(x == null); + } +}"; + + var comp = CreateStandardCompilation(source, references: new[] { ValueTupleRef, SystemRuntimeFacadeRef }); + comp.VerifyDiagnostics( + // (6,17): error CS8304: The first operand of an 'as' operator may not be a tuple literal without a natural type. + // var x = (0, null) as (int, T)?; + Diagnostic(ErrorCode.ERR_TypelessTupleInAs, "(0, null) as (int, T)?").WithLocation(6, 17) + ); + } } } \ No newline at end of file diff --git a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb index 05b2da99f587c..63170568905e5 100644 --- a/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb +++ b/src/Compilers/VisualBasic/Test/Emit/CodeGen/CodeGenTuples.vb @@ -7863,6 +7863,336 @@ BC30512: Option Strict On disallows implicit conversions from 'Double' to 'Strin End Sub + + Public Sub TupleCTypeNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleDirectCastNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleTryCastNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30792: 'TryCast' operand must be reference type, but '(Integer, String)?' is a value type. + Dim x As (Integer, String)? = TryCast((1, Nothing), (Integer, String)?) + ~~~~~~~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(node).Kind) + + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleTryCastNullableConversionWithTypelessTuple2() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C(Of Integer, T)'. + Dim x = TryCast((0, Nothing), C(Of Integer, T)) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(0, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(node).Kind) + + Assert.Equal("C(Of System.Int32, T)", model.GetTypeInfo(node.Parent).Type.ToTestDisplayString()) + Assert.Equal("C(Of System.Int32, T)", model.GetTypeInfo(node.Parent).ConvertedType.ToTestDisplayString()) + + End Sub + + + Public Sub TupleImplicitNullableConversionWithTypelessTuple() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertNoDiagnostics() + CompileAndVerify(comp, expectedOutput:="(1, )") + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim node = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().Single() + Assert.Equal("(1, Nothing)", node.ToString()) + Assert.Null(model.GetTypeInfo(node).Type) + Assert.Equal("System.Nullable(Of (System.Int32, System.String))", model.GetTypeInfo(node).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningNullableTuple, model.GetConversion(node).Kind) + + End Sub + + + Public Sub ImplicitConversionOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y As C? = (2, Nothing) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("C", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.Narrowing Or ConversionKind.UserDefined, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("System.Nullable(Of C)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.DelegateRelaxationLevelNone, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub DirectCastOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C'. + Dim x = DirectCast((1, Nothing), C) + ~~~~~~~~~~~~ +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y = DirectCast((2, Nothing), C?) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub TryCastOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30792: 'TryCast' operand must be reference type, but 'C' is a value type. + Dim x = TryCast((1, Nothing), C) + ~ +BC30792: 'TryCast' operand must be reference type, but 'C?' is a value type. + Dim y = TryCast((2, Nothing), C?) + ~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + + + Public Sub CTypeOnTypelessTupleWithUserConversion() + Dim comp = CreateCompilationWithMscorlibAndVBRuntime( + + +, additionalRefs:=s_valueTupleRefs, options:=TestOptions.DebugExe) + + comp.AssertTheseDiagnostics( +BC30311: Value of type '(Integer, Object)' cannot be converted to 'C?'. + Dim y = CType((2, Nothing), C?) + ~~~~~~~~~~~~ + ) + + Dim tree = comp.SyntaxTrees.Single() + Dim model = comp.GetSemanticModel(tree) + Dim firstTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(0) + Assert.Equal("(1, Nothing)", firstTuple.ToString()) + Assert.Null(model.GetTypeInfo(firstTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(firstTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(firstTuple).Kind) + + Dim secondTuple = tree.GetRoot().DescendantNodes().OfType(Of TupleExpressionSyntax)().ElementAt(1) + Assert.Equal("(2, Nothing)", secondTuple.ToString()) + Assert.Null(model.GetTypeInfo(secondTuple).Type) + Assert.Equal("(System.Int32, System.Object)", model.GetTypeInfo(secondTuple).ConvertedType.ToTestDisplayString()) + Assert.Equal(ConversionKind.WideningTuple, model.GetConversion(secondTuple).Kind) + + End Sub + Public Sub TupleTargetTypeLambda() @@ -8261,6 +8591,35 @@ End Module End Sub + + + + Public Sub GetSymbolInfo_01() + Dim source = " + Class C + Shared Sub Main() + Dim x1 = (Alice:=1, ""hello"") + + Dim Alice = x1.Alice + End Sub +End Class + " + + Dim tree = Parse(source, options:=TestOptions.Regular) + Dim comp = CreateCompilationWithMscorlib(tree) + + Dim model = comp.GetSemanticModel(tree, ignoreAccessibility:=False) + Dim nodes = tree.GetCompilationUnitRoot().DescendantNodes() + + Dim nc = nodes.OfType(Of NameColonEqualsSyntax)().ElementAt(0) + + Dim sym = model.GetSymbolInfo(nc.Name) + + Assert.Equal("Alice", sym.Symbol.Name) + Assert.Equal(SymbolKind.Field, sym.Symbol.Kind) ' Incorrectly returns Local + Assert.Equal(nc.Name.GetLocation(), sym.Symbol.Locations(0)) ' Incorrect location + End Sub + Public Sub RetargetTupleErrorType() Dim libComp = CreateCompilationWithMscorlibAndVBRuntime( diff --git a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb index ee0735d40dc86..5d369567f276e 100644 --- a/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb +++ b/src/EditorFeatures/VisualBasicTest/CodeActions/InlineTemporary/InlineTemporaryTests.vb @@ -4397,6 +4397,29 @@ Class C } End Sub End Class +" + Await TestInRegularAndScriptAsync(code, expected, ignoreTrivia:=False) + End Function + + + + Public Async Function TupleElementNameIsNotReplaced() As Task + ' The name of the named element has bad symbol info and gets replaced with (1 + 2) + Dim code = " +Class C + Sub M() + Dim [||]i = 1 + 2 + Dim t = (i, i:=3) + End Sub +End Class +" + + Dim expected = " +Class C + Sub M() + Dim t = (1 + 2, i:=3) + End Sub +End Class " Await TestInRegularAndScriptAsync(code, expected, ignoreTrivia:=False) End Function From 9fecc12da9657a2e3b9230b4fdf001cc7b9d50a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Mon, 8 May 2017 14:42:10 -0700 Subject: [PATCH 214/214] Avoid calling GetDiagnostics on compilation during EnC analysis (#19167) Avoid calling GetDiagnostics on compilation during EnC analysis --- .../EditAndContinue/EditAndContinueTests.cs | 125 ++++++++++ .../Compilation/GetSemanticInfoTests.cs | 45 ++++ .../EditAndContinue/EditAndContinueTests.vb | 104 +++++++++ .../CSharpEditAndContinueAnalyzerTests.cs | 44 +++- .../EditAndContinue/Helpers/Extensions.cs | 25 +- .../EditAndContinue/RudeEditStatementTests.cs | 90 +++++++- .../EditAndContinue/RudeEditTopLevelTests.cs | 214 ++++++++++++++---- .../EditAndContinueTestHelpers.cs | 18 +- .../EditAndContinue/Helpers/Extensions.vb | 33 ++- .../EditAndContinue/RudeEditStatementTests.vb | 91 +++++--- .../EditAndContinue/RudeEditTopLevelTests.vb | 173 +++++++++----- ...VisualBasicEditAndContinueAnalyzerTests.vb | 36 ++- .../CSharpEditAndContinueAnalyzer.cs | 2 +- .../AbstractEditAndContinueAnalyzer.cs | 123 ++++++++-- .../VisualBasicEditAndContinueAnalyzer.vb | 2 +- 15 files changed, 936 insertions(+), 189 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 85712044bdcfe..61acbdd66d93a 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -92,6 +92,131 @@ public void Delta_AssemblyDefTable() Assert.False(diff1.GetMetadata().Reader.IsAssembly); } + [Fact] + public void SemanticErrors_MethodBody() + { + var source0 = MarkedSource(@" +class C +{ + static void E() + { + int x = 1; + System.Console.WriteLine(x); + } + + static void G() + { + System.Console.WriteLine(1); + } +}"); + var source1 = MarkedSource(@" +class C +{ + static void E() + { + int x = Unknown(2); + System.Console.WriteLine(x); + } + + static void G() + { + System.Console.WriteLine(2); + } +}"); + + var compilation0 = CreateStandardCompilation(source0.Tree, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + + var e0 = compilation0.GetMember("C.E"); + var e1 = compilation1.GetMember("C.E"); + var g0 = compilation0.GetMember("C.G"); + var g1 = compilation1.GetMember("C.G"); + + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + // Semantic errors are reported only for the bodies of members being emitted. + + var diffError = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + new SemanticEdit(SemanticEditKind.Update, e0, e1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + diffError.EmitResult.Diagnostics.Verify( + // (6,17): error CS0103: The name 'Unknown' does not exist in the current context + // int x = Unknown(2); + Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown").WithArguments("Unknown").WithLocation(6, 17)); + + var diffGood = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + new SemanticEdit(SemanticEditKind.Update, g0, g1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + diffGood.EmitResult.Diagnostics.Verify(); + + diffGood.VerifyIL(@"C.G", @" +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""void System.Console.WriteLine(int)"" + IL_0007: nop + IL_0008: ret +} +"); + } + + [Fact] + public void SemanticErrors_Declaration() + { + var source0 = MarkedSource(@" +class C +{ + static void G() + { + System.Console.WriteLine(1); + } +} +"); + var source1 = MarkedSource(@" +class C +{ + static void G() + { + System.Console.WriteLine(2); + } +} + +class Bad : Bad +{ +} +"); + + var compilation0 = CreateStandardCompilation(source0.Tree, options: ComSafeDebugDll); + var compilation1 = compilation0.WithSource(source1.Tree); + + var g0 = compilation0.GetMember("C.G"); + var g1 = compilation1.GetMember("C.G"); + + var v0 = CompileAndVerify(compilation0); + var md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var generation0 = EmitBaseline.CreateInitialBaseline(md0, v0.CreateSymReader().GetEncMethodDebugInfo); + + var diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + new SemanticEdit(SemanticEditKind.Update, g0, g1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables: true))); + + // All declaration errors are reported regardless of what member do we emit. + + diff.EmitResult.Diagnostics.Verify( + // (10,7): error CS0146: Circular base class dependency involving 'Bad' and 'Bad' + // class Bad : Bad + Diagnostic(ErrorCode.ERR_CircularBase, "Bad").WithArguments("Bad", "Bad").WithLocation(10, 7)); + } + [Fact] public void ModifyMethod() { diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs index 8892410f5f472..dadc6bd62976d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/GetSemanticInfoTests.cs @@ -5766,6 +5766,51 @@ partial void MyPartialMethod(MyUndefinedMethod m) Assert.Equal(2, errs.Count()); } + [Fact] + public void PartialTypeDiagnostics_Constructors() + { + var file1 = @" +partial class C +{ + C() {} +} +"; + + var file2 = @" +partial class C +{ + C() {} +} +"; + var file3 = @" +partial class C +{ + C() {} +} +"; + + var tree1 = Parse(file1); + var tree2 = Parse(file2); + var tree3 = Parse(file3); + var comp = CreateStandardCompilation(new[] { tree1, tree2, tree3 }); + var model1 = comp.GetSemanticModel(tree1); + var model2 = comp.GetSemanticModel(tree2); + var model3 = comp.GetSemanticModel(tree3); + + model1.GetDeclarationDiagnostics().Verify(); + + model2.GetDeclarationDiagnostics().Verify( + // (4,5): error CS0111: Type 'C' already defines a member called '.ctor' with the same parameter types + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments(".ctor", "C").WithLocation(4, 5)); + + model3.GetDeclarationDiagnostics().Verify( + // (4,5): error CS0111: Type 'C' already defines a member called '.ctor' with the same parameter types + Diagnostic(ErrorCode.ERR_MemberAlreadyExists, "C").WithArguments(".ctor", "C").WithLocation(4, 5)); + + Assert.Equal(3, comp.GlobalNamespace.GetMember("C").InstanceConstructors.Length); + } + + [WorkItem(1076661, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1076661")] [Fact] public void Bug1076661() diff --git a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb index e7dda329a77fd..7542cc44b18b0 100644 --- a/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb +++ b/src/Compilers/VisualBasic/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.vb @@ -18,6 +18,107 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UnitTests Public Class EditAndContinueTests Inherits EditAndContinueTestBase + + Public Sub SemanticErrors_MethodBody() + Dim source0 = MarkedSource(" +Class C + Shared Sub E() + Dim x As Integer = 1 + System.Console.WriteLine(x) + End Sub + + Shared Sub G() + System.Console.WriteLine(1) + End Sub +End Class +") + Dim source1 = MarkedSource(" +Class C + Shared Sub E() + Dim x = Unknown(2) + System.Console.WriteLine(x) + End Sub + + Shared Sub G() + System.Console.WriteLine(2) + End Sub +End Class +") + Dim compilation0 = CreateCompilationWithMscorlib(source0.Tree, options:=ComSafeDebugDll) + Dim compilation1 = compilation0.WithSource(source1.Tree) + + Dim e0 = compilation0.GetMember(Of MethodSymbol)("C.E") + Dim e1 = compilation1.GetMember(Of MethodSymbol)("C.E") + Dim g0 = compilation0.GetMember(Of MethodSymbol)("C.G") + Dim g1 = compilation1.GetMember(Of MethodSymbol)("C.G") + + Dim v0 = CompileAndVerify(compilation0) + Dim md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData) + Dim generation0 = EmitBaseline.CreateInitialBaseline(md0, AddressOf v0.CreateSymReader().GetEncMethodDebugInfo) + + ' Semantic errors are reported only for the bodies of members being emitted. + Dim diffError = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, e0, e1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) + + diffError.EmitResult.Diagnostics.Verify( + Diagnostic(ERRID.ERR_NameNotDeclared1, "Unknown").WithArguments("Unknown").WithLocation(4, 17)) + + Dim diffGood = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, g0, g1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) + + diffGood.EmitResult.Diagnostics.Verify() + diffGood.VerifyIL("C.G", " +{ + // Code size 9 (0x9) + .maxstack 1 + IL_0000: nop + IL_0001: ldc.i4.2 + IL_0002: call ""Sub System.Console.WriteLine(Integer)"" + IL_0007: nop + IL_0008: ret +}") + End Sub + + + Public Sub SemanticErrors_Declaration() + Dim source0 = MarkedSource(" +Class C + Sub G() + System.Console.WriteLine(1) + End Sub +End Class +") + Dim source1 = MarkedSource(" +Class C + Sub G() + System.Console.WriteLine(1) + End Sub +End Class + +Class Bad + Inherits Bad +End Class +") + Dim compilation0 = CreateCompilationWithMscorlib(source0.Tree, options:=ComSafeDebugDll) + Dim compilation1 = compilation0.WithSource(source1.Tree) + + Dim g0 = compilation0.GetMember(Of MethodSymbol)("C.G") + Dim g1 = compilation1.GetMember(Of MethodSymbol)("C.G") + + Dim v0 = CompileAndVerify(compilation0) + Dim md0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData) + Dim generation0 = EmitBaseline.CreateInitialBaseline(md0, AddressOf v0.CreateSymReader().GetEncMethodDebugInfo) + + Dim diff = compilation1.EmitDifference( + generation0, + ImmutableArray.Create(New SemanticEdit(SemanticEditKind.Update, g0, g1, GetSyntaxMapFromMarkers(source0, source1), preserveLocalVariables:=True))) + + diff.EmitResult.Diagnostics.Verify( + Diagnostic(ERRID.ERR_TypeInItsInheritsClause1, "Bad").WithArguments("Bad").WithLocation(9, 12)) + End Sub + Public Sub ModifyMethod_WithTuples() Dim source0 = @@ -5183,5 +5284,8 @@ End Class") } ") End Sub + + + End Class End Namespace diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index 15845eed701aa..288f7948c828d 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -496,8 +496,8 @@ public static void Main() } } - [Fact] - public async Task AnalyzeDocumentAsync_SemanticError_Change() + [Fact, WorkItem(10683, "https://github.com/dotnet/roslyn/issues/10683")] + public async Task AnalyzeDocumentAsync_SemanticErrorInMethodBody_Change() { string source1 = @" class C @@ -521,6 +521,46 @@ public static void Main() "; var analyzer = new CSharpEditAndContinueAnalyzer(); + using (var workspace = TestWorkspace.CreateCSharp(source1)) + { + var documentId = workspace.CurrentSolution.Projects.First().Documents.First().Id; + var oldSolution = workspace.CurrentSolution; + var newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)); + + var baseActiveStatements = ImmutableArray.Create(); + var result = await analyzer.AnalyzeDocumentAsync(oldSolution, baseActiveStatements, newSolution.GetDocument(documentId), default(CancellationToken)); + + Assert.True(result.HasChanges); + + // no declaration errors (error in method body is only reported when emitting): + Assert.False(result.HasChangesAndErrors); + Assert.False(result.HasChangesAndCompilationErrors); + } + } + + [Fact, WorkItem(10683, "https://github.com/dotnet/roslyn/issues/10683")] + public async Task AnalyzeDocumentAsync_SemanticErrorInDeclaration_Change() + { + string source1 = @" +class C +{ + public static void Main(Bar x) + { + System.Console.WriteLine(1); + } +} +"; + string source2 = @" +class C +{ + public static void Main(Bar x) + { + System.Console.WriteLine(2); + } +} +"; + var analyzer = new CSharpEditAndContinueAnalyzer(); + using (var workspace = TestWorkspace.CreateCSharp(source1)) { var documentId = workspace.CurrentSolution.Projects.First().Documents.First().Id; diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs index 11943c1ccf4e9..877df16e72d2e 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/Extensions.cs @@ -5,6 +5,7 @@ using Microsoft.CodeAnalysis.EditAndContinue; using Microsoft.CodeAnalysis.EditAndContinue.UnitTests; using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.EditAndContinue; +using Microsoft.CodeAnalysis.Test.Utilities; namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { @@ -58,16 +59,34 @@ internal static void VerifySemanticDiagnostics( this EditScript editScript, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - VerifySemantics(editScript, ActiveStatementsDescription.Empty, null, expectedDiagnostics); + VerifySemanticDiagnostics(editScript, null, expectedDiagnostics); + } + + internal static void VerifySemanticDiagnostics( + this EditScript editScript, + DiagnosticDescription expectedDeclarationError, + params RudeEditDiagnosticDescription[] expectedDiagnostics) + { + VerifySemantics(editScript, ActiveStatementsDescription.Empty, null, expectedDeclarationError, expectedDiagnostics); + } + + internal static void VerifySemantics( + this EditScript editScript, + ActiveStatementsDescription activeStatements, + SemanticEditDescription[] expectedSemanticEdits, + params RudeEditDiagnosticDescription[] expectedDiagnostics) + { + VerifySemantics(editScript, activeStatements, expectedSemanticEdits, null, expectedDiagnostics); } internal static void VerifySemantics( this EditScript editScript, ActiveStatementsDescription activeStatements, SemanticEditDescription[] expectedSemanticEdits, + DiagnosticDescription expectedDeclarationError, params RudeEditDiagnosticDescription[] expectedDiagnostics) { - VerifySemantics(editScript, activeStatements, null, null, expectedSemanticEdits, expectedDiagnostics); + VerifySemantics(editScript, activeStatements, null, null, expectedSemanticEdits, expectedDeclarationError, expectedDiagnostics); } internal static void VerifySemantics( @@ -76,6 +95,7 @@ internal static void VerifySemantics( IEnumerable additionalOldSources, IEnumerable additionalNewSources, SemanticEditDescription[] expectedSemanticEdits, + DiagnosticDescription expectedDeclarationError, params RudeEditDiagnosticDescription[] expectedDiagnostics) { CSharpEditAndContinueTestHelpers.Instance.VerifySemantics( @@ -84,6 +104,7 @@ internal static void VerifySemantics( additionalOldSources, additionalNewSources, expectedSemanticEdits, + expectedDeclarationError, expectedDiagnostics); } } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs index 50a769bea0d33..152b61439997e 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditStatementTests.cs @@ -4416,7 +4416,7 @@ void F() } // Add corresponding test to VB - [WpfFact(Skip = "TODO")] + [Fact(Skip = "TODO")] public void Lambdas_Update_Signature_CustomModifiers1() { var delegateSource = @" @@ -4502,6 +4502,43 @@ void F() edits.VerifySemanticDiagnostics(); } + [Fact] + public void Lambdas_Signature_SemanticErrors() + { + var src1 = @" +using System; + +class C +{ + void G(Func f) {} + + void F() + { + G(a => 1); + } +} +"; + var src2 = @" +using System; + +class C +{ + void G(Func f) {} + + void F() + { + G(a => 2); + } +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + // (6,17): error CS0246: The type or namespace name 'Unknown' could not be found (are you missing a using directive or an assembly reference?) + // void G(Func f) {} + Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "Unknown").WithArguments("Unknown").WithLocation(6, 17)); + } + [Fact] public void Lambdas_Update_DelegateType1() { @@ -7530,6 +7567,7 @@ static IEnumerable F() null, null, null, + null, new[] { Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "static IEnumerable F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute") @@ -8111,12 +8149,13 @@ static async Task F() var edits = GetTopEdits(src1, src2); CSharpEditAndContinueTestHelpers.InstanceMinAsync.VerifySemantics( - edits, - ActiveStatementsDescription.Empty, - null, - null, - null, - new[] + editScript: edits, + activeStatements: ActiveStatementsDescription.Empty, + additionalNewSources: null, + additionalOldSources: null, + expectedSemanticEdits: null, + expectedDeclarationError: null, + expectedDiagnostics: new[] { Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "static async Task F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute") }); @@ -8159,6 +8198,43 @@ static async Task F() null); } + [Fact] + public void SemanticError_AwaitInPropertyAccessor() + { + string src1 = @" +using System.Threading.Tasks; + +class C +{ + public Task P + { + get + { + await Task.Delay(1); + return 1; + } + } +} +"; + string src2 = @" +using System.Threading.Tasks; + +class C +{ + public Task P + { + get + { + await Task.Delay(2); + return 1; + } + } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics(); + } + #endregion #region Out Var diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditTopLevelTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditTopLevelTests.cs index 926c182fa6e7b..d2aa6170d44f5 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditTopLevelTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/RudeEditTopLevelTests.cs @@ -3853,13 +3853,14 @@ public void StaticCtor_Partial_Delete() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single()) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3874,13 +3875,14 @@ public void InstanceCtor_Partial_DeletePrivate() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single()) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3895,13 +3897,14 @@ public void InstanceCtor_Partial_DeletePublic() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single()) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3916,11 +3919,12 @@ public void InstanceCtor_Partial_DeletePrivateToPublic() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - null, - Diagnostic(RudeEditKind.Delete, "partial class C", FeaturesResources.constructor)); + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: null, + expectedDiagnostics: new[] { Diagnostic(RudeEditKind.Delete, "partial class C", FeaturesResources.constructor) }, + expectedDeclarationError: null); } [Fact] @@ -3935,13 +3939,14 @@ public void StaticCtor_Partial_Insert() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").StaticConstructors.Single(), preserveLocalVariables: true) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3956,13 +3961,14 @@ public void InstanceCtor_Partial_InsertPublic() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3977,13 +3983,14 @@ public void InstanceCtor_Partial_InsertPrivate() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -3998,13 +4005,14 @@ public void InstanceCtor_Partial_InsertInternal() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - new[] + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true) - }); + }, + expectedDeclarationError: null); } [Fact] @@ -4019,11 +4027,12 @@ public void InstanceCtor_Partial_InsertPrivateToPublic() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - null, - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "public C()")); + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits: null, + expectedDiagnostics: new[] { Diagnostic(RudeEditKind.ChangingConstructorVisibility, "public C()") }, + expectedDeclarationError: null); } [Fact] @@ -4038,11 +4047,12 @@ public void InstanceCtor_Partial_InsertPrivateToInternal() var edits = GetTopEdits(srcA1, srcA2); edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { srcB1 }, - new[] { srcB2 }, - null, - Diagnostic(RudeEditKind.ChangingConstructorVisibility, "internal C()")); + activeStatements: ActiveStatementsDescription.Empty, + additionalOldSources: new[] { srcB1 }, + additionalNewSources: new[] { srcB2 }, + expectedSemanticEdits:null, + expectedDiagnostics: new[] { Diagnostic(RudeEditKind.ChangingConstructorVisibility, "internal C()") }, + expectedDeclarationError: null); } [Fact] @@ -4313,6 +4323,76 @@ public void Insert_ExternConstruct() Diagnostic(RudeEditKind.InsertExtern, "public extern C()", FeaturesResources.constructor)); } + [Fact(Skip = "https://github.com/dotnet/roslyn/pull/18940")] + public void ParameterlessConstructor_SemanticError_Delete1() + { + string src1 = @" +class C +{ + D() {} +} +"; + string src2 = @" +class C +{ +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics(); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/pull/18940")] + public void ParameterlessConstructor_SemanticError_Delete_OutsideOfClass1() + { + string src1 = @" +C() {} +"; + string src2 = @" +"; + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics(); + } + + [Fact] + public void Constructor_SemanticError_Partial() + { + string src1 = @" +partial class C +{ + partial void C(int x); +} + +partial class C +{ + partial void C(int x) + { + System.Console.WriteLine(1); + } +} +"; + string src2 = @" +partial class C +{ + partial void C(int x); +} + +partial class C +{ + partial void C(int x) + { + System.Console.WriteLine(2); + } +} +"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemanticDiagnostics( + // (4,18): error CS0542: 'C': member names cannot be the same as their enclosing type + // partial void C(int x); + Diagnostic(ErrorCode.ERR_MemberNameSameAsType, "C").WithArguments("C").WithLocation(4, 18)); + } + #endregion #region Fields and Properties with Initializers @@ -5622,6 +5702,40 @@ class C }); } + [Fact] + public void PropertyWithInitializer_SemanticError_Partial() + { + string src1 = @" +partial class C +{ + partial int P => 1; +} + +partial class C +{ + partial int P => 1; +} +"; + string src2 = @" +partial class C +{ + partial int P => 1; +} + +partial class C +{ + partial int P => 1; + + public C() { } +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemanticDiagnostics( + // (4,17): error CS0753: Only methods, classes, structs, or interfaces may be partial + // partial int P => 1; + Diagnostic(ErrorCode.ERR_PartialMethodOnlyMethods, "P").WithLocation(4, 17)); + } + #endregion #region Fields diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index c64ac7fd1b24d..c3865e799b69d 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -205,6 +205,7 @@ internal void VerifySemantics( IEnumerable additionalOldSources = null, IEnumerable additionalNewSources = null, SemanticEditDescription[] expectedSemanticEdits = null, + DiagnosticDescription expectedDeclarationError = null, RudeEditDiagnosticDescription[] expectedDiagnostics = null) { var editMap = Analyzer.BuildEditMap(editScript); @@ -234,18 +235,6 @@ internal void VerifySemantics( var oldCompilation = CreateLibraryCompilation("Old", oldTrees); var newCompilation = CreateLibraryCompilation("New", newTrees); - if (oldCompilation is CSharpCompilation) - { - oldCompilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify(); - newCompilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).Verify(); - } - else - { - // TODO: verify all compilation diagnostics like C# does (tests need to be updated) - oldTrees.SelectMany(tree => tree.GetDiagnostics()).Where(d => d.Severity == DiagnosticSeverity.Error).Verify(); - newTrees.SelectMany(tree => tree.GetDiagnostics()).Where(d => d.Severity == DiagnosticSeverity.Error).Verify(); - } - var oldModel = oldCompilation.GetSemanticModel(oldRoot.SyntaxTree); var newModel = newCompilation.GetSemanticModel(newRoot.SyntaxTree); @@ -297,8 +286,13 @@ internal void VerifySemantics( newModel, actualSemanticEdits, diagnostics, + out var firstDeclarationErrorOpt, default(CancellationToken)); + var actualDeclarationErrors = (firstDeclarationErrorOpt != null) ? new[] { firstDeclarationErrorOpt } : Array.Empty(); + var expectedDeclarationErrors = (expectedDeclarationError != null) ? new[] { expectedDeclarationError } : Array.Empty(); + actualDeclarationErrors.Verify(expectedDeclarationErrors); + diagnostics.Verify(newSource, expectedDiagnostics); if (expectedSemanticEdits == null) diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb index 8190e3ef3cf52..156d1ff6cf1c7 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/Extensions.vb @@ -5,6 +5,7 @@ Imports Microsoft.CodeAnalysis.Differencing Imports Microsoft.CodeAnalysis.EditAndContinue Imports Microsoft.CodeAnalysis.EditAndContinue.UnitTests Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue +Imports Microsoft.CodeAnalysis.Test.Utilities Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests @@ -47,7 +48,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Friend Sub VerifySemanticDiagnostics(editScript As EditScript(Of SyntaxNode), ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VerifySemantics(editScript, ActiveStatementsDescription.Empty, Nothing, expectedDiagnostics) + VerifySemanticDiagnostics(editScript, Nothing, expectedDiagnostics) + End Sub + + + Friend Sub VerifySemanticDiagnostics(editScript As EditScript(Of SyntaxNode), + expectedDeclarationError As DiagnosticDescription, + ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) + VerifySemantics(editScript, ActiveStatementsDescription.Empty, Nothing, expectedDeclarationError, expectedDiagnostics) End Sub @@ -55,7 +63,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests activeStatements As ActiveStatementsDescription, expectedSemanticEdits As SemanticEditDescription(), ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) - VerifySemantics(editScript, activeStatements, Nothing, Nothing, expectedSemanticEdits, expectedDiagnostics) + VerifySemantics(editScript, activeStatements, Nothing, Nothing, expectedSemanticEdits, Nothing, expectedDiagnostics) + End Sub + + + Friend Sub VerifySemantics(editScript As EditScript(Of SyntaxNode), + activeStatements As ActiveStatementsDescription, + expectedSemanticEdits As SemanticEditDescription(), + expectedDeclarationError As DiagnosticDescription, + ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) + VerifySemantics(editScript, activeStatements, Nothing, Nothing, expectedSemanticEdits, expectedDeclarationError, expectedDiagnostics) + End Sub + + + Friend Sub VerifySemantics(editScript As EditScript(Of SyntaxNode), + activeStatements As ActiveStatementsDescription, + additionalOldSources As IEnumerable(Of String), + additionalNewSources As IEnumerable(Of String), + expectedSemanticEdits As SemanticEditDescription(), + ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) + VerifySemantics(editScript, activeStatements, additionalOldSources, additionalNewSources, expectedSemanticEdits, Nothing, expectedDiagnostics) End Sub @@ -64,6 +91,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests additionalOldSources As IEnumerable(Of String), additionalNewSources As IEnumerable(Of String), expectedSemanticEdits As SemanticEditDescription(), + expectedDeclarationError As DiagnosticDescription, ParamArray expectedDiagnostics As RudeEditDiagnosticDescription()) VisualBasicEditAndContinueTestHelpers.Instance.VerifySemantics( editScript, @@ -71,6 +99,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests additionalOldSources, additionalNewSources, expectedSemanticEdits, + expectedDeclarationError, expectedDiagnostics) End Sub End Module diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb index dda84edfbf765..5b08ad8d7f037 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditStatementTests.vb @@ -3418,7 +3418,7 @@ End Class Dim src1 = " Imports System Class C - Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) + Readonly Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) Get Return New Func(Of Integer, Integer)(Function(a3) a1 + a2) End Get @@ -3428,7 +3428,7 @@ End Class Dim src2 = " Imports System Class C - Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) + Readonly Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) Get Return New Func(Of Integer, Integer)(Function(a3) a2) End Get @@ -3714,7 +3714,7 @@ End Class Dim src1 = " Imports System Class C - Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) + Readonly Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) Get Return New Func(Of Integer, Integer)(Function(a3) a2) End Get @@ -3724,7 +3724,7 @@ End Class Dim src2 = " Imports System Class C - Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) + Readonly Property Item(a1 As Integer, a2 As Integer) As Func(Of Integer, Integer) Get Return New Func(Of Integer, Integer)(Function(a3) a1 + a2) End Get @@ -4041,12 +4041,12 @@ Imports System Partial Class C Dim x As Integer = 1 - Partial Sub F() ' def + Private Partial Sub F() ' def End Sub End Class Partial Class C - Partial Sub F() ' impl + Private Sub F() ' impl Dim f = New Func(Of Integer, Integer)(Function(a) a) End Sub End Class @@ -4056,19 +4056,19 @@ Imports System Partial Class C Dim x As Integer = 1 - Partial Sub F() ' def + Private Partial Sub F() ' def End Sub End Class Partial Class C - Partial Sub F() ' impl + Private Sub F() ' impl Dim f = New Func(Of Integer, Integer)(Function(a) a + x) End Sub End Class " Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.CapturingVariable, "F", "Me").WithFirstLine("Partial Sub F() ' impl")) + Diagnostic(RudeEditKind.CapturingVariable, "F", "Me").WithFirstLine("Private Sub F() ' impl")) End Sub @@ -4497,6 +4497,40 @@ End Class edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.RenamingCapturedVariable, "y", "x", "y")) End Sub + + + Public Sub Lambdas_Signature_SemanticErrors() + Dim src1 = " +Imports System + +Class C + + Sub G(f As Func(Of Unknown, Unknown)) + End Sub + + Sub F() + G(Function(a) 1) + End Sub +End Class +" + Dim src2 = " +Imports System + +Class C + + Sub G(f As Func(Of Unknown, Unknown)) + End Sub + + Sub F() + G(Function(a) 2) + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(ERRID.ERR_UndefinedType1, "Unknown").WithArguments("Unknown").WithLocation(6, 24)) + End Sub + #End Region #Region "Queries" @@ -5976,12 +6010,13 @@ End Class " Dim edits = GetTopEdits(src1, src2) VisualBasicEditAndContinueTestHelpers.Instance40.VerifySemantics( - edits, - ActiveStatementsDescription.Empty, - Nothing, - Nothing, - Nothing, - {Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}) + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Iterator Function F()", "System.Runtime.CompilerServices.IteratorStateMachineAttribute")}, + expectedDeclarationError:=Nothing) End Sub @@ -6006,12 +6041,13 @@ End Class " Dim edits = GetTopEdits(src1, src2) VisualBasicEditAndContinueTestHelpers.Instance40.VerifySemantics( - edits, - ActiveStatementsDescription.Empty, - Nothing, - Nothing, - Nothing, - Nothing) + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:=Nothing, + expectedDeclarationError:=Nothing) End Sub #End Region @@ -6092,12 +6128,13 @@ End Class " Dim edits = GetTopEdits(src1, src2) VisualBasicEditAndContinueTestHelpers.InstanceMinAsync.VerifySemantics( - edits, - ActiveStatementsDescription.Empty, - Nothing, - Nothing, - Nothing, - {Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}) + editScript:=edits, + activeStatements:=ActiveStatementsDescription.Empty, + additionalOldSources:=Nothing, + additionalNewSources:=Nothing, + expectedSemanticEdits:=Nothing, + expectedDiagnostics:={Diagnostic(RudeEditKind.UpdatingStateMachineMethodMissingAttribute, "Shared Async Function F()", "System.Runtime.CompilerServices.AsyncStateMachineAttribute")}, + expectedDeclarationError:=Nothing) End Sub diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb index 029a86a725198..9008acc521dbb 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/RudeEditTopLevelTests.vb @@ -1308,7 +1308,7 @@ End Class Public Sub NestedClass_InsertMemberWithInitializer1() Dim src1 = "Public Class C : End Class" - Dim src2 = "Public Class C : Private Class D : Public Property P As New List(Of String) : End Class : End Class" + Dim src2 = "Public Class C : Private Class D : Public Property P As New Object : End Class : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, @@ -1318,7 +1318,7 @@ End Class Public Sub NestedClass_InsertMemberWithInitializer2() Dim src1 = "Public Module C : End Module" - Dim src2 = "Public Module C : Private Class D : Property P As New List(Of String) : End Class : End Module" + Dim src2 = "Public Module C : Private Class D : Property P As New Object : End Class : End Module" Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, @@ -1767,14 +1767,14 @@ End Class Public Sub MethodInsert_PrivateWithAttribute() Dim src1 = "Class C : End Class" - Dim src2 = "Class C : " & vbLf & "Private Sub F : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Private Sub F : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [Private Sub F : End Sub]@11", - "Insert [Private Sub F]@11", - "Insert []@11", - "Insert [A]@12") + "Insert [Private Sub F : End Sub]@11", + "Insert [Private Sub F]@11", + "Insert []@11", + "Insert [System.Obsolete]@12") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"))}) @@ -2926,7 +2926,8 @@ Partial Class C Return 0 End Function - Dim A1(F(Function(a1) a1 + 1)), A2 As Integer = F(Function(a2) a2 + 1) + Dim A1(F(Function(a1) a1 + 1)) + Dim A2 As Integer = F(Function(a2) a2 + 1) Dim A3, A4 As New Func(Of Integer, Integer)(Function(a34) a34 + 1) Dim A5(F(Function(a51) a51 + 1), F(Function(a52) a52 + 1)) As Integer End Class @@ -2947,7 +2948,8 @@ Partial Class C Return 0 End Function - Dim A1(F(Function(a1) a1 + 1)), A2 As Integer = F(Function(a2) a2 + 1) + Dim A1(F(Function(a1) a1 + 1)) + Dim A2 As Integer = F(Function(a2) a2 + 1) Dim A3, A4 As New Func(Of Integer, Integer)(Function(a34) a34 + 1) Dim A5(F(Function(a51) a51 + 1), F(Function(a52) a52 + 1)) As Integer End Class @@ -3181,6 +3183,39 @@ End Class ' {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C").Constructors.Single(), syntaxMap(0))}) End Sub + + + Public Sub Constructor_SemanticError_Partial() + Dim src1 = " +Partial Class C + Partial Sub New(x As Integer) + End Sub +End Class + +Class C + Partial Sub New(x As Integer) + System.Console.WriteLine(1) + End Sub +End Class + +" + Dim src2 = " +Partial Class C + Partial Sub New(x As Integer) + End Sub +End Class + +Class C + Partial Sub New(x As Integer) + System.Console.WriteLine(2) + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(ERRID.ERR_ConstructorCannotBeDeclaredPartial, "Partial").WithArguments("Partial").WithLocation(3, 5)) + End Sub + #End Region #Region "Declare" @@ -4205,19 +4240,6 @@ End Class {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub - - Public Sub Field_InitializerUpdate2() - Dim src1 = "Class C : Dim a, b As Integer = 0 : End Class" - Dim src2 = "Class C : Dim a, b As Integer = 1 : End Class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [a, b As Integer = 0]@14 -> [a, b As Integer = 1]@14") - - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) - End Sub - Public Sub Property_Instance_InitializerUpdate() Dim src1 = "Class C : Property a As Integer = 0 : End Class" @@ -4259,12 +4281,12 @@ End Class Public Sub Field_InitializerUpdate_AsNew1() - Dim src1 = "Class C : Dim a As New D(1) : End Class" - Dim src2 = "Class C : Dim a As New D(2) : End Class" + Dim src1 = "Class C : Dim a As New Decimal(1) : End Class" + Dim src2 = "Class C : Dim a As New Decimal(2) : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [a As New D(1)]@14 -> [a As New D(2)]@14") + "Update [a As New Decimal(1)]@14 -> [a As New Decimal(2)]@14") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) @@ -4272,12 +4294,12 @@ End Class Public Sub Field_InitializerUpdate_AsNew2() - Dim src1 = "Class C : Dim a, b As New C(1) : End Class" - Dim src2 = "Class C : Dim a, b As New C(2) : End Class" + Dim src1 = "Class C : Dim a, b As New Decimal(1) : End Class" + Dim src2 = "Class C : Dim a, b As New Decimal(2) : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [a, b As New C(1)]@14 -> [a, b As New C(2)]@14") + "Update [a, b As New Decimal(1)]@14 -> [a, b As New Decimal(2)]@14") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) @@ -4285,12 +4307,12 @@ End Class Public Sub Property_InitializerUpdate_AsNew() - Dim src1 = "Class C : Property a As New D(1) : End Class" - Dim src2 = "Class C : Property a As New D(2) : End Class" + Dim src1 = "Class C : Property a As New Decimal(1) : End Class" + Dim src2 = "Class C : Property a As New Decimal(2) : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Property a As New D(1)]@10 -> [Property a As New D(2)]@10") + "Update [Property a As New Decimal(1)]@10 -> [Property a As New Decimal(2)]@10") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) @@ -4348,19 +4370,6 @@ End Class {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub - - Public Sub Property_StructInitializerUpdate_Delete() - Dim src1 = "Structure C : Property a As Integer = 0 : End Structure" - Dim src2 = "Structure C : Property a As Integer : End Structure" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [Property a As Integer = 0]@14 -> [Property a As Integer]@14") - - edits.VerifySemantics(ActiveStatementsDescription.Empty, - {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) - End Sub - Public Sub Field_InitializerUpdate_Insert() Dim src1 = "Class C : Dim a As Integer : End Class" @@ -4436,15 +4445,15 @@ End Class Public Sub FieldUpdate_ModuleCtorUpdate1() - Dim src1 = "Module C : Dim a As Integer : " & vbLf & "Shared Sub New() : End Sub : End Module" + Dim src1 = "Module C : Dim a As Integer : " & vbLf & "Sub New() : End Sub : End Module" Dim src2 = "Module C : Dim a As Integer = 0 : End Module" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( "Update [a As Integer]@15 -> [a As Integer = 0]@15", - "Delete [Shared Sub New() : End Sub]@31", - "Delete [Shared Sub New()]@31", - "Delete [()]@45") + "Delete [Sub New() : End Sub]@31", + "Delete [Sub New()]@31", + "Delete [()]@38") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").SharedConstructors.Single())}) @@ -4560,8 +4569,8 @@ End Class Public Sub PropertyUpdate_ModuleCtorUpdate2() - Dim src1 = "Module C : Property a As Integer : " & vbLf & "Shared Sub New() : End Sub : End Module" - Dim src2 = "Module C : Property a As Integer = 0 : " & vbLf & "Shared Sub New() : End Sub : End Module" + Dim src1 = "Module C : Property a As Integer : " & vbLf & "Sub New() : End Sub : End Module" + Dim src2 = "Module C : Property a As Integer = 0 : " & vbLf & "Sub New() : End Sub : End Module" Dim edits = GetTopEdits(src1, src2) edits.VerifySemantics(ActiveStatementsDescription.Empty, @@ -4966,11 +4975,11 @@ End Class Public Sub PrivateFieldInsert2() Dim src1 = "Class C : Private a As Integer = 1 : End Class" - Dim src2 = "Class C : Private a, b As Integer = 1 : End Class" + Dim src2 = "Class C : Private a, b As Integer : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [a As Integer = 1]@18 -> [a, b As Integer = 1]@18", + "Update [a As Integer = 1]@18 -> [a, b As Integer]@18", "Insert [b]@21") edits.VerifySemantics(ActiveStatementsDescription.Empty, @@ -5860,6 +5869,51 @@ End Class" {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(0), syntaxMap(0)), SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").Constructors(1), syntaxMap(0))}) End Sub + + + Public Sub PropertyWithInitializer_SemanticError_Partial() + Dim src1 = " +Partial Class C + Partial Public ReadOnly Property NewProperty() As String + Get + Return 1 + End Get + End Property +End Class + +Partial Class C + Partial Public ReadOnly Property NewProperty() As String + Get + Return 1 + End Get + End Property +End Class +" + Dim src2 = " +Partial Class C + Partial Public ReadOnly Property NewProperty() As String + Get + Return 1 + End Get + End Property +End Class + +Partial Class C + Partial Public ReadOnly Property NewProperty() As String + Get + Return 1 + End Get + End Property + + Sub New() + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemanticDiagnostics( + Diagnostic(ERRID.ERR_BadPropertyFlags1, "Partial").WithArguments("Partial").WithLocation(3, 5)) + End Sub + #End Region #Region "Events" @@ -5950,29 +6004,32 @@ End Class" Public Sub EventInsert_IntoLayoutClass_Sequential() - Dim src1 = Class C End Class -]]>.Value - Dim src2 = Class C Private Custom Event c As Action - AddHandler + AddHandler(value As Action) End AddHandler - RemoveHandler + RemoveHandler(value As Action) End RemoveHandler + + RaiseEvent() + End RaiseEvent End Event End Class -]]>.Value +" Dim edits = GetTopEdits(src1, src2) edits.VerifySemanticDiagnostics() End Sub diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index 4efae5fee2da2..35e6f7c488fd0 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -543,8 +543,8 @@ End Class End Using End Function - - Public Async Function AnalyzeDocumentAsync_SemanticError_Change() As Threading.Tasks.Task + + Public Async Function AnalyzeDocumentAsync_SemanticErrorInMethodBody_Change() As Task Dim source1 = " Class C Public Shared Sub Main() @@ -571,6 +571,38 @@ End Class Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatementSpan)() Dim result = Await analyzer.AnalyzeDocumentAsync(oldSolution, baseActiveStatements, newSolution.GetDocument(documentId), Nothing) + ' no declaration errors (error in method body is only reported when emitting) + Assert.False(result.HasChangesAndErrors) + Assert.False(result.HasChangesAndCompilationErrors) + End Using + End Function + + + Public Async Function AnalyzeDocumentAsync_SemanticErrorInDeclaration_Change() As Task + Dim source1 = " +Class C + Public Shared Sub Main(x As Bar) + System.Console.WriteLine(1) + End Sub +End Class +" + Dim source2 = " +Class C + Public Shared Sub Main(x As Bar) + System.Console.WriteLine(2) + End Sub +End Class +" + + Dim analyzer = New VisualBasicEditAndContinueAnalyzer() + Using workspace = TestWorkspace.CreateVisualBasic(source1) + Dim documentId = workspace.CurrentSolution.Projects.First().Documents.First().Id + Dim oldSolution = workspace.CurrentSolution + Dim newSolution = workspace.CurrentSolution.WithDocumentText(documentId, SourceText.From(source2)) + + Dim baseActiveStatements = ImmutableArray.Create(Of ActiveStatementSpan)() + Dim result = Await analyzer.AnalyzeDocumentAsync(oldSolution, baseActiveStatements, newSolution.GetDocument(documentId), Nothing) + Assert.True(result.HasChanges) Assert.True(result.HasChangesAndErrors) Assert.True(result.HasChangesAndCompilationErrors) diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index a60a91374a1ba..153e12affca1b 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -3271,7 +3271,7 @@ private static bool DeclareSameIdentifiers(SyntaxToken[] oldVariables, SyntaxTok return true; } - internal override void ReportSemanticRudeEdits(SemanticModel oldModel, SyntaxNode oldNode, SemanticModel newModel, SyntaxNode newNode, List diagnostics) + internal override void ReportMemberBodySemanticRudeEdits(SemanticModel oldModel, SyntaxNode oldNode, SemanticModel newModel, SyntaxNode newNode, List diagnostics) { var foundNode = FindUnsupportedV7Switch(oldModel, oldNode, diagnostics); if (foundNode != null) diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index d8f76241727ea..0a3e386585189 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -23,8 +23,6 @@ internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyz { internal abstract bool ExperimentalFeaturesEnabled(SyntaxTree tree); - internal abstract void ReportSemanticRudeEdits(SemanticModel oldModel, SyntaxNode oldNode, SemanticModel newModel, SyntaxNode newNode, List diagnostics); - /// /// Finds a member declaration node containing given active statement node. /// @@ -244,6 +242,8 @@ protected abstract bool TryMatchActiveStatement( internal abstract void ReportInsertedMemberSymbolRudeEdits(List diagnostics, ISymbol newSymbol); internal abstract void ReportStateMachineSuspensionPointRudeEdits(List diagnostics, SyntaxNode oldNode, SyntaxNode newNode); + internal abstract void ReportMemberBodySemanticRudeEdits(SemanticModel oldModel, SyntaxNode oldMemberBody, SemanticModel newModel, SyntaxNode newMemberBody, List diagnostics); + internal abstract bool IsMethod(SyntaxNode declaration); internal abstract bool IsLambda(SyntaxNode node); internal abstract bool IsLambdaExpression(SyntaxNode node); @@ -337,7 +337,7 @@ public async Task AnalyzeDocumentAsync( var trackingService = baseSolution.Workspace.Services.GetService(); cancellationToken.ThrowIfCancellationRequested(); - + // TODO: newTree.HasErrors? var syntaxDiagnostics = newRoot.GetDiagnostics(); var syntaxErrorCount = syntaxDiagnostics.Count(d => d.Severity == DiagnosticSeverity.Error); @@ -438,19 +438,6 @@ public async Task AnalyzeDocumentAsync( cancellationToken.ThrowIfCancellationRequested(); - // Bail if there were any semantic errors in the compilation. - var newCompilation = await document.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false); - var firstError = newCompilation.GetDiagnostics(cancellationToken).FirstOrDefault(d => d.Severity == DiagnosticSeverity.Error); - if (firstError != null) - { - var location = firstError.Location; - DocumentAnalysisResults.Log.Write("Semantic errors, first: {0}", location.IsInSource ? location.SourceTree.FilePath : location.MetadataModule.Name); - - return DocumentAnalysisResults.Errors(newActiveStatements.AsImmutable(), ImmutableArray.Create(), hasSemanticErrors: true); - } - - cancellationToken.ThrowIfCancellationRequested(); - var triviaEdits = new List>(); var lineEdits = new List(); @@ -472,6 +459,8 @@ public async Task AnalyzeDocumentAsync( return DocumentAnalysisResults.Errors(newActiveStatements.AsImmutable(), diagnostics.AsImmutable()); } + cancellationToken.ThrowIfCancellationRequested(); + List semanticEdits = null; if (syntacticEdits.Edits.Length > 0 || triviaEdits.Count > 0) { @@ -490,10 +479,19 @@ public async Task AnalyzeDocumentAsync( newModel, semanticEdits, diagnostics, + out var firstDeclaratingErrorOpt, cancellationToken); cancellationToken.ThrowIfCancellationRequested(); + if (firstDeclaratingErrorOpt != null) + { + var location = firstDeclaratingErrorOpt.Location; + DocumentAnalysisResults.Log.Write("Declaration errors, first: {0}", location.IsInSource ? location.SourceTree.FilePath : location.MetadataModule.Name); + + return DocumentAnalysisResults.Errors(newActiveStatements.AsImmutable(), ImmutableArray.Create(), hasSemanticErrors: true); + } + if (diagnostics.Count > 0) { DocumentAnalysisResults.Log.Write("{0}@{1}: semantic rude edit ({2} total)", document.FilePath, diagnostics.First().Span.Start, diagnostics.Count); @@ -2095,6 +2093,7 @@ internal void AnalyzeSemantics( SemanticModel newModel, [Out]List semanticEdits, [Out]List diagnostics, + out Diagnostic firstDeclarationErrorOpt, CancellationToken cancellationToken) { // { new type -> constructor update } @@ -2104,6 +2103,7 @@ internal void AnalyzeSemantics( INamedTypeSymbol layoutAttribute = null; var newSymbolsWithEdit = new HashSet(); int updatedMemberIndex = 0; + firstDeclarationErrorOpt = null; for (int i = 0; i < editScript.Edits.Length; i++) { cancellationToken.ThrowIfCancellationRequested(); @@ -2135,7 +2135,9 @@ internal void AnalyzeSemantics( continue; } - // Deleting an parameterless constructor needs special handling: + // The only member that is allowed to be deleted is a parameterless constructor. + // For any other member a rude edit is reported earlier during syntax edit classification. + // Deleting a parameterless constructor needs special handling. // If the new type has a parameterless ctor of the same accessibility then UPDATE. // Error otherwise. @@ -2208,12 +2210,24 @@ internal void AnalyzeSemantics( Debug.Assert(oldType != null); Debug.Assert(newType != null); + // Validate that the type declarations are correct. If not we can't reason about their members. + // Declaration diagnostics are cached on compilation, so we don't need to cache them here. + firstDeclarationErrorOpt = + GetFirstDeclarationError(oldModel, oldType, cancellationToken) ?? + GetFirstDeclarationError(newModel, newType, cancellationToken); + + if (firstDeclarationErrorOpt != null) + { + continue; + } + // Inserting a parameterless constructor needs special handling: // 1) static ctor // a) old type has an implicit static ctor // UPDATE of the implicit static ctor // b) otherwise // INSERT of a static parameterless ctor + // // 2) public instance ctor // a) old type has an implicit instance ctor // UPDATE of the implicit instance ctor @@ -2305,16 +2319,31 @@ internal void AnalyzeSemantics( continue; } - ReportSemanticRudeEdits(oldModel, edit.OldNode, newModel, edit.NewNode, diagnostics); - oldSymbol = GetSymbolForEdit(oldModel, edit.OldNode, edit.Kind, editMap, cancellationToken); Debug.Assert((newSymbol == null) == (oldSymbol == null)); + var oldContainingType = oldSymbol.ContainingType; + var newContainingType = newSymbol.ContainingType; + + // Validate that the type declarations are correct to avoid issues with invalid partial declarations, etc. + // Declaration diagnostics are cached on compilation, so we don't need to cache them here. + firstDeclarationErrorOpt = + GetFirstDeclarationError(oldModel, oldContainingType, cancellationToken) ?? + GetFirstDeclarationError(newModel, newContainingType, cancellationToken); + + if (firstDeclarationErrorOpt != null) + { + continue; + } + if (updatedMemberIndex < updatedMembers.Count && updatedMembers[updatedMemberIndex].EditOrdinal == i) { var updatedMember = updatedMembers[updatedMemberIndex]; + ReportMemberBodySemanticRudeEdits(oldModel, updatedMember.OldBody, newModel, updatedMember.NewBody, diagnostics); + ReportStateMachineRudeEdits(oldModel.Compilation, updatedMember, oldSymbol, diagnostics); + ReportLambdaAndClosureRudeEdits( oldModel, updatedMember.OldBody, @@ -2360,8 +2389,8 @@ internal void AnalyzeSemantics( IsDeclarationWithInitializer(edit.NewNode)) { if (DeferConstructorEdit( - oldSymbol.ContainingType, - newSymbol.ContainingType, + oldContainingType, + newContainingType, editKind, edit.NewNode, newSymbol, @@ -2394,6 +2423,19 @@ internal void AnalyzeSemantics( { var oldSymbol = GetSymbolForEdit(oldModel, edit.Key, EditKind.Update, editMap, cancellationToken); var newSymbol = GetSymbolForEdit(newModel, edit.Value, EditKind.Update, editMap, cancellationToken); + var oldContainingType = oldSymbol.ContainingType; + var newContainingType = newSymbol.ContainingType; + + // Validate that the type declarations are correct to avoid issues with invalid partial declarations, etc. + // Declaration diagnostics are cached on compilation, so we don't need to cache them here. + firstDeclarationErrorOpt = + GetFirstDeclarationError(oldModel, oldContainingType, cancellationToken) ?? + GetFirstDeclarationError(newModel, newContainingType, cancellationToken); + + if (firstDeclarationErrorOpt != null) + { + continue; + } // We need to provide syntax map to the compiler if the member is active (see member update above): bool isActiveMember = @@ -2412,8 +2454,8 @@ internal void AnalyzeSemantics( IsDeclarationWithInitializer(edit.Value)) { if (DeferConstructorEdit( - oldSymbol.ContainingType, - newSymbol.ContainingType, + oldContainingType, + newContainingType, SemanticEditKind.Update, edit.Value, newSymbol, @@ -2466,6 +2508,31 @@ internal void AnalyzeSemantics( } } + private Diagnostic GetFirstDeclarationError(SemanticModel primaryModel, ISymbol symbol, CancellationToken cancellationToken) + { + foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) + { + SemanticModel model; + if (primaryModel.SyntaxTree == syntaxReference.SyntaxTree) + { + model = primaryModel; + } + else + { + model = primaryModel.Compilation.GetSemanticModel(syntaxReference.SyntaxTree, ignoreAccessibility: false); + } + + var diagnostics = model.GetDeclarationDiagnostics(syntaxReference.Span, cancellationToken); + var firstError = diagnostics.FirstOrDefault(d => d.Severity == DiagnosticSeverity.Error); + if (firstError != null) + { + return firstError; + } + } + + return null; + } + #region Type Layout Update Validation internal void ReportTypeLayoutUpdateRudeEdits( @@ -3684,8 +3751,14 @@ private void ReportStateMachineRudeEdits( return; } - // only methods and anonymous functions may be async/iterators machines: - var stateMachineAttributeQualifiedName = ((IMethodSymbol)oldMember).IsAsync ? + // Only methods and anonymous functions can be async/iterators machines, + // but don't assume so to be resiliant against errors in code. + if (!(oldMember is IMethodSymbol oldMethod)) + { + return; + } + + var stateMachineAttributeQualifiedName = oldMethod.IsAsync ? "System.Runtime.CompilerServices.AsyncStateMachineAttribute" : "System.Runtime.CompilerServices.IteratorStateMachineAttribute"; diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 9e2c5e681ddf0..f88df8ad5e749 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -3173,7 +3173,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue DirectCast(n2.ForOrForEachStatement, ForEachStatementSyntax).ControlVariable)) End Sub - Friend Overrides Sub ReportSemanticRudeEdits(oldModel As SemanticModel, oldNode As SyntaxNode, newModel As SemanticModel, newNode As SyntaxNode, diagnostics As List(Of RudeEditDiagnostic)) + Friend Overrides Sub ReportMemberBodySemanticRudeEdits(oldModel As SemanticModel, oldNode As SyntaxNode, newModel As SemanticModel, newNode As SyntaxNode, diagnostics As List(Of RudeEditDiagnostic)) End Sub #End Region