From d5562d50cd253c027677a87367e85329f443242c Mon Sep 17 00:00:00 2001 From: David Poeschl Date: Tue, 11 Apr 2017 15:28:32 -0700 Subject: [PATCH 001/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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/165] 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 81b8036b39a43f6889e9974bf21e439b8063c71f Mon Sep 17 00:00:00 2001 From: Manish Vasani Date: Tue, 25 Apr 2017 04:57:57 -0700 Subject: [PATCH 009/165] 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 010/165] 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 7f1aee3b7ecff18f710b780cb5a9485598e6eff0 Mon Sep 17 00:00:00 2001 From: Tomas Matousek Date: Fri, 28 Apr 2017 14:04:41 -0700 Subject: [PATCH 011/165] 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 012/165] 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 013/165] 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 014/165] 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 015/165] 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 016/165] 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 017/165] 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 018/165] 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 019/165] 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 020/165] 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 021/165] 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 022/165] 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 023/165] 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 024/165] 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 025/165] 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 026/165] 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 027/165] 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 028/165] 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 029/165] 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 030/165] 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 031/165] 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 032/165] 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 033/165] 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 034/165] 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 035/165] 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 036/165] 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 037/165] 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 038/165] 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 039/165] 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 040/165] 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 041/165] 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 042/165] 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 043/165] 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 044/165] 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 045/165] 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 046/165] 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 047/165] 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 048/165] 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 049/165] 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 64d06fd81bf296f03bc4157a7ffa90c82839b743 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Tue, 2 May 2017 10:34:39 -0500 Subject: [PATCH 050/165] 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 f0f77ed7502cfb6cba86a2fd59033fb41398d09b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Tue, 2 May 2017 12:30:58 -0700 Subject: [PATCH 051/165] 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 052/165] 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 053/165] 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 054/165] 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 5228fd8154fee47465779e6786afaa38f5fc73d7 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Tue, 2 May 2017 13:51:33 -0700 Subject: [PATCH 055/165] 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 056/165] 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 057/165] 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 058/165] 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 059/165] 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 060/165] 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 061/165] 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 062/165] 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 063/165] 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 064/165] 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 065/165] 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 066/165] 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 067/165] 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 068/165] 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 069/165] 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 070/165] 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 071/165] 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 072/165] 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 073/165] 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 074/165] 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 075/165] 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 076/165] 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 4e3db2b7a0732d45a720e9ed00c00cd22ab67a14 Mon Sep 17 00:00:00 2001 From: Tom Meschter Date: Wed, 3 May 2017 10:59:57 -0700 Subject: [PATCH 077/165] 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 078/165] 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 079/165] 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 080/165] 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 081/165] 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 082/165] 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 083/165] 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 097/165] 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 098/165] 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 099/165] 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 100/165] 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 101/165] 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 102/165] 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 103/165] 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 104/165] 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 105/165] 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 106/165] 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 107/165] 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 108/165] 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 109/165] 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 110/165] 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 111/165] 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 112/165] 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 113/165] 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 114/165] 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 115/165] 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 116/165] 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 117/165] 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 118/165] 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 119/165] 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 120/165] 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 b9a3e8254db1088abddf4c490d6ab72bf303dd6b Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 19:38:30 -0700 Subject: [PATCH 121/165] 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 122/165] 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 ebc112baca89eabc6f41834f56e1258f2582cb83 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Thu, 4 May 2017 21:49:38 -0700 Subject: [PATCH 123/165] 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 124/165] 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 125/165] 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 126/165] 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 127/165] 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 128/165] 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 129/165] 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 130/165] 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 d66839db52902c8cfe3f45c5e47f6212b2cbb6c4 Mon Sep 17 00:00:00 2001 From: Julien Couvreur Date: Sat, 6 May 2017 12:32:57 -0700 Subject: [PATCH 131/165] 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 132/165] 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 133/165] 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 134/165] 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 135/165] 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 de0b9b01a3884909916763f6773064365dfc26a2 Mon Sep 17 00:00:00 2001 From: CyrusNajmabadi Date: Sat, 6 May 2017 16:11:01 -0700 Subject: [PATCH 136/165] 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 137/165] 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 138/165] 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 139/165] 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 140/165] 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 141/165] 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 142/165] 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 143/165] 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 144/165] 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 145/165] 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 163/165] 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 164/165] 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)" /> - +