From 0f61ae686d29de27b48a61fcc0f50479e9c06ecf Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 17:10:05 -0700 Subject: [PATCH 01/19] Include ExplicitInterfaceImplementation --- .../AbstractInheritanceMarginService.cs | 4 +- .../InheritanceMarginTests.cs | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index cd522588ef52f..1dd5e510df5ac 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -92,8 +92,8 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) return false; } - if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol || - symbol.IsOrdinaryMethod()) + if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol + || symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) { return true; } diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 7f1c087caf2ba..8527060f8d614 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -989,6 +989,65 @@ public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar itemForFooInBar2); } + [Fact] + public Task TestCSharpExplicitInterfaceImplementation() + { + var lessThanToken = SecurityElement.Escape("<"); + var greaterThanToken = SecurityElement.Escape(">"); + var markup = $@" +interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} +{{ + void {{|target3:Foo|}}(T t); +}} + +abstract class {{|target1:AbsBar|}} : IBar{lessThanToken}int{greaterThanToken} +{{ + void IBar{lessThanToken}int{greaterThanToken}.{{|target4:Foo|}}(int t) + {{ + throw new System.NotImplementedException(); + }} +}}"; + var itemForIBar = new TestInheritanceMemberItem( + lineNumber: 2, + memberName: "interface IBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "class AbsBar", + locationTag: "target1", + relationship: InheritanceRelationship.Implemented))); + + var itemForFooInIBar = new TestInheritanceMemberItem( + lineNumber: 4, + memberName: "void IBar.Foo(T)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void AbsBar.IBar.Foo(int)", + locationTag: "target4", + relationship: InheritanceRelationship.Implemented))); + + var itemForAbsBar = new TestInheritanceMemberItem( + lineNumber: 7, + memberName: "class AbsBar", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "interface IBar", + locationTag: "target2", + relationship: InheritanceRelationship.Implementing))); + + var itemForFooInAbsBar = new TestInheritanceMemberItem( + lineNumber: 9, + memberName: "void AbsBar.IBar.Foo(int)", + targets: ImmutableArray.Create(new TargetInfo( + targetSymbolDisplayName: "void IBar.Foo(T)", + locationTag: "target3", + relationship: InheritanceRelationship.Implementing))); + + return VerifyInSingleDocumentAsync( + markup, + LanguageNames.CSharp, + itemForIBar, + itemForFooInIBar, + itemForAbsBar, + itemForFooInAbsBar); + } + #endregion #region TestsForVisualBasic From 5e4fb6def5774c7126b67f28ca3eb18296181e2f Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 17:15:07 -0700 Subject: [PATCH 02/19] Fix formatting --- .../Core/InheritanceMargin/AbstractInheritanceMarginService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs index 1dd5e510df5ac..b48d1535a71f8 100644 --- a/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs +++ b/src/EditorFeatures/Core/InheritanceMargin/AbstractInheritanceMarginService.cs @@ -93,7 +93,7 @@ private static bool CanHaveInheritanceTarget(ISymbol symbol) } if (symbol is INamedTypeSymbol or IEventSymbol or IPropertySymbol - || symbol is IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) + or IMethodSymbol { MethodKind: MethodKind.Ordinary or MethodKind.ExplicitInterfaceImplementation }) { return true; } From c350df2f3afada773b92622399d40ada9c5634a6 Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Wed, 9 Jun 2021 23:28:24 -0700 Subject: [PATCH 03/19] Put escapse logic into helpers --- .../InheritanceMarginTests.cs | 53 ++++++++++--------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index 8527060f8d614..cb369cf1509f3 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -22,6 +22,8 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin public class InheritanceMarginTests { private const string SearchAreaTag = "SeachTag"; + private static readonly string s_lessThanToken = SecurityElement.Escape("<"); + private static readonly string s_greaterThanToken = SecurityElement.Escape(">"); #region Helpers @@ -33,6 +35,9 @@ private static Task VerifyInSingleDocumentAsync( string languageName, params TestInheritanceMemberItem[] memberItems) { + // Escapse < and > for xml + markup = markup.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken); + var workspaceFile = $@" @@ -138,12 +143,12 @@ private static async Task VerifyInDifferentProjectsAsync( Assembly2 - {markup1.markupInProject1} + {markup1.markupInProject1.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} - {markup2.markupInProject2} + {markup2.markupInProject2.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} "; @@ -933,18 +938,16 @@ public class {|target5:Bar2|} : Bar1, IBar [Fact] public Task TestCSharpFindGenericsBaseType() { - var lessThanToken = SecurityElement.Escape("<"); - var greaterThanToken = SecurityElement.Escape(">"); - var markup = $@" -public interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -{{ - void {{|target4:Foo|}}(); -}} + var markup = @" +public interface {|target2:IBar|} +{ + void {|target4:Foo|}(); +} -public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar{lessThanToken}string{greaterThanToken} -{{ - public void {{|target3:Foo|}}(); -}}"; +public class {|target1:Bar2|} : IBar, IBar +{ + public void {|target3:Foo|}(); +}"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, @@ -992,21 +995,19 @@ public class {{|target1:Bar2|}} : IBar{lessThanToken}int{greaterThanToken}, IBar [Fact] public Task TestCSharpExplicitInterfaceImplementation() { - var lessThanToken = SecurityElement.Escape("<"); - var greaterThanToken = SecurityElement.Escape(">"); - var markup = $@" -interface {{|target2:IBar|}}{lessThanToken}T{greaterThanToken} -{{ - void {{|target3:Foo|}}(T t); -}} + var markup = @" +interface {|target2:IBar|} +{ + void {|target3:Foo|}(T t); +} -abstract class {{|target1:AbsBar|}} : IBar{lessThanToken}int{greaterThanToken} -{{ - void IBar{lessThanToken}int{greaterThanToken}.{{|target4:Foo|}}(int t) - {{ +abstract class {|target1:AbsBar|} : IBar +{ + void IBar.{|target4:Foo|}(int t) + { throw new System.NotImplementedException(); - }} -}}"; + } +}"; var itemForIBar = new TestInheritanceMemberItem( lineNumber: 2, memberName: "interface IBar", From 6e42437557c6c1415461a4bc8345eb8b4eab9b7d Mon Sep 17 00:00:00 2001 From: Shen Chen Date: Thu, 10 Jun 2021 00:39:43 -0700 Subject: [PATCH 04/19] Use CDATA --- .../InheritanceMargin/InheritanceMarginTests.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs index cb369cf1509f3..5dd0a86442d4c 100644 --- a/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs +++ b/src/EditorFeatures/Test/InheritanceMargin/InheritanceMarginTests.cs @@ -4,7 +4,6 @@ using System.Collections.Immutable; using System.Linq; -using System.Security; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -22,8 +21,6 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.InheritanceMargin public class InheritanceMarginTests { private const string SearchAreaTag = "SeachTag"; - private static readonly string s_lessThanToken = SecurityElement.Escape("<"); - private static readonly string s_greaterThanToken = SecurityElement.Escape(">"); #region Helpers @@ -35,8 +32,8 @@ private static Task VerifyInSingleDocumentAsync( string languageName, params TestInheritanceMemberItem[] memberItems) { - // Escapse < and > for xml - markup = markup.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken); + markup = @$""; var workspaceFile = $@" @@ -143,12 +140,14 @@ private static async Task VerifyInDifferentProjectsAsync( Assembly2 - {markup1.markupInProject1.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} + - {markup2.markupInProject2.Replace("<", s_lessThanToken).Replace(">", s_greaterThanToken)} + "; From aabf33afb468dd02ece70698c5259e641b08efb2 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 14:07:32 +0100 Subject: [PATCH 05/19] Add TestIncrementInExpressionContext C# test --- .../UseCompoundAssignmentTests.cs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index 65769a72b907f..9f1adb72c152b 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -1091,6 +1091,27 @@ void M() { } } +}"); + } + + [WorkItem(38054, "https://github.com/dotnet/roslyn/issues/53969")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseCompoundAssignment)] + public async Task TestIncrementInExpressionContext() + { + await TestInRegularAndScript1Async( +@"public class C +{ + void M(int i) + { + M(i [||]= i + 1); + } +}", +@"public class C +{ + void M(int i) + { + M(++i); + } }"); } } From 4edd63f092e379c7cc2ad8ac70124d97d3f37b1e Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 14:08:21 +0100 Subject: [PATCH 06/19] Use Postfix operator only if in statement context. --- ...arpUseCompoundAssignmentCodeFixProvider.cs | 22 ++++++++++++------- ...actUseCompoundAssignmentCodeFixProvider.cs | 8 +++---- ...sicUseCompoundAssignmentCodeFixProvider.vb | 4 ++-- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs index 7734fba6a5bb6..3ab74e9b9b6fc 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs @@ -30,14 +30,20 @@ protected override AssignmentExpressionSyntax Assignment( return SyntaxFactory.AssignmentExpression(assignmentOpKind, left, syntaxToken, right); } - protected override ExpressionSyntax Increment(ExpressionSyntax left) - { - return SyntaxFactory.PostfixUnaryExpression(SyntaxKind.PostIncrementExpression, left); - } + protected override ExpressionSyntax Increment(ExpressionSyntax left, bool postfix) + => postfix + ? Postfix(SyntaxKind.PostIncrementExpression, left) + : Prefix(SyntaxKind.PreIncrementExpression, left); - protected override ExpressionSyntax Decrement(ExpressionSyntax left) - { - return SyntaxFactory.PostfixUnaryExpression(SyntaxKind.PostDecrementExpression, left); - } + protected override ExpressionSyntax Decrement(ExpressionSyntax left, bool postfix) + => postfix + ? Postfix(SyntaxKind.PostDecrementExpression, left) + : Prefix(SyntaxKind.PreDecrementExpression, left); + + private static ExpressionSyntax Postfix(SyntaxKind kind, ExpressionSyntax operand) + => SyntaxFactory.PostfixUnaryExpression(kind, operand); + + private static ExpressionSyntax Prefix(SyntaxKind kind, ExpressionSyntax operand) + => SyntaxFactory.PrefixUnaryExpression(kind, operand); } } diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index e3609df5b8128..e02c78e08dcbe 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -41,8 +41,8 @@ protected AbstractUseCompoundAssignmentCodeFixProvider( protected abstract SyntaxToken Token(TSyntaxKind kind); protected abstract TAssignmentSyntax Assignment( TSyntaxKind assignmentOpKind, TExpressionSyntax left, SyntaxToken syntaxToken, TExpressionSyntax right); - protected abstract TExpressionSyntax Increment(TExpressionSyntax left); - protected abstract TExpressionSyntax Decrement(TExpressionSyntax left); + protected abstract TExpressionSyntax Increment(TExpressionSyntax left, bool postfix); + protected abstract TExpressionSyntax Decrement(TExpressionSyntax left, bool postfix); public override Task RegisterCodeFixesAsync(CodeFixContext context) { @@ -81,12 +81,12 @@ protected override Task FixAllAsync( if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Increment)) { - return Increment((TExpressionSyntax)leftOfAssign); + return Increment((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsStatement(currentAssignment.Parent)); } if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Decrement)) { - return Decrement((TExpressionSyntax)leftOfAssign); + return Decrement((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsStatement(currentAssignment.Parent)); } var assignmentOpKind = _binaryToAssignmentMap[syntaxKinds.Convert(rightOfAssign.RawKind)]; diff --git a/src/Analyzers/VisualBasic/CodeFixes/UseCompoundAssignment/VisualBasicUseCompoundAssignmentCodeFixProvider.vb b/src/Analyzers/VisualBasic/CodeFixes/UseCompoundAssignment/VisualBasicUseCompoundAssignmentCodeFixProvider.vb index 4848d738e4558..578a8bbd85a35 100644 --- a/src/Analyzers/VisualBasic/CodeFixes/UseCompoundAssignment/VisualBasicUseCompoundAssignmentCodeFixProvider.vb +++ b/src/Analyzers/VisualBasic/CodeFixes/UseCompoundAssignment/VisualBasicUseCompoundAssignmentCodeFixProvider.vb @@ -30,11 +30,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseCompoundAssignment Return SyntaxFactory.AssignmentStatement(assignmentOpKind, left, syntaxToken, right) End Function - Protected Overrides Function Increment(left As ExpressionSyntax) As ExpressionSyntax + Protected Overrides Function Increment(left As ExpressionSyntax, postfix As Boolean) As ExpressionSyntax Throw ExceptionUtilities.Unreachable End Function - Protected Overrides Function Decrement(left As ExpressionSyntax) As ExpressionSyntax + Protected Overrides Function Decrement(left As ExpressionSyntax, postfix As Boolean) As ExpressionSyntax Throw ExceptionUtilities.Unreachable End Function End Class From d7199c5b9bb99a6d8b5549533f43cd9458de560a Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 17:52:25 +0100 Subject: [PATCH 07/19] Use IsSimpleAssignmentStatement and add some more tests --- .../UseCompoundAssignmentTests.cs | 55 ++++++++++++++++++- ...actUseCompoundAssignmentCodeFixProvider.cs | 4 +- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index 9f1adb72c152b..a270cf16619fd 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -1087,7 +1087,7 @@ void M() { void M() { - for (int i = 0; i < 10; i++) + for (int i = 0; i < 10; ++i) { } } @@ -1114,5 +1114,58 @@ void M(int i) } }"); } + + [WorkItem(38054, "https://github.com/dotnet/roslyn/issues/53969")] + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseCompoundAssignment)] + [InlineData("switch($$) { }")] + [InlineData("while(($$) > 0) { }")] + [InlineData("_ = true ? $$ : 0;")] + [InlineData("_ = ($$);")] + public async Task TestPrefixIncrement1(string expressionContext) + { + var before = expressionContext.Replace("$$", "i [||]= i + 1"); + var after = expressionContext.Replace("$$", "++i"); + await TestInRegularAndScript1Async( +@$"public class C +{{ + void M(int i) + {{ + {before} + }} +}}", +@$"public class C +{{ + void M(int i) + {{ + {after} + }} +}}"); + } + + [WorkItem(38054, "https://github.com/dotnet/roslyn/issues/53969")] + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseCompoundAssignment)] + [InlineData("return $$;")] + [InlineData("return true ? $$ : 0;")] + [InlineData("return ($$);")] + public async Task TestPrefixIncrement2(string expressionContext) + { + var before = expressionContext.Replace("$$", "i [||]= i + 1"); + var after = expressionContext.Replace("$$", "++i"); + await TestInRegularAndScript1Async( +@$"public class C +{{ + int M(int i) + {{ + {before} + }} +}}", +@$"public class C +{{ + int M(int i) + {{ + {after} + }} +}}"); + } } } diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index e02c78e08dcbe..1784cf7dae4d6 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -81,12 +81,12 @@ protected override Task FixAllAsync( if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Increment)) { - return Increment((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsStatement(currentAssignment.Parent)); + return Increment((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); } if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Decrement)) { - return Decrement((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsStatement(currentAssignment.Parent)); + return Decrement((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); } var assignmentOpKind = _binaryToAssignmentMap[syntaxKinds.Convert(rightOfAssign.RawKind)]; From 9e428fe75443bded413b9e966423ab15c716d789 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 19:00:27 +0100 Subject: [PATCH 08/19] Use WithTriviaFrom(currentAssignment) and add tests --- .../UseCompoundAssignmentTests.cs | 30 +++++++++++++++++++ ...actUseCompoundAssignmentCodeFixProvider.cs | 4 +-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index a270cf16619fd..e19e878e0a740 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -1160,6 +1160,36 @@ int M(int i) }} }}", @$"public class C +{{ + int M(int i) + {{ + {after} + }} +}}"); + } + + [WorkItem(38054, "https://github.com/dotnet/roslyn/issues/53969")] + [Theory, Trait(Traits.Feature, Traits.Features.CodeActionsUseCompoundAssignment)] + [InlineData( + "/* Before */ i [||]= i + 1; /* After */", + "/* Before */ i++; /* After */")] + [InlineData( + "M( /* Before */ i [||]= i + 1 /* After */ );", + "M( /* Before */ ++i /* After */ );")] + [InlineData( + "M( /* Before */ i [||]= i - 1 /* After */ );", + "M( /* Before */ --i /* After */ );")] + public async Task TestTriviaPreserved(string before, string after) + { + await TestInRegularAndScript1Async( +@$"public class C +{{ + int M(int i) + {{ + {before} + }} +}}", +@$"public class C {{ int M(int i) {{ diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index 1784cf7dae4d6..3c6ec77d4ff6d 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -81,12 +81,12 @@ protected override Task FixAllAsync( if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Increment)) { - return Increment((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); + return Increment((TExpressionSyntax)leftOfAssign.WithTriviaFrom(currentAssignment), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); } if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Decrement)) { - return Decrement((TExpressionSyntax)leftOfAssign.WithoutTrailingTrivia(), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); + return Decrement((TExpressionSyntax)leftOfAssign.WithTriviaFrom(currentAssignment), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); } var assignmentOpKind = _binaryToAssignmentMap[syntaxKinds.Convert(rightOfAssign.RawKind)]; From 8b0e6572676c85ad1a6b88fb244f3c784f994fef Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 20:36:22 +0100 Subject: [PATCH 09/19] for statement increment as postfix added --- .../UseCompoundAssignmentTests.cs | 2 +- ...ractUseCompoundAssignmentCodeFixProvider.cs | 18 ++++++++++++++++-- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 15 +++++++++++++++ .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 9 +++++++++ .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 10 ++++++++++ 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs index e19e878e0a740..e428fd8b5da3a 100644 --- a/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs +++ b/src/Analyzers/CSharp/Tests/UseCompoundAssignment/UseCompoundAssignmentTests.cs @@ -1087,7 +1087,7 @@ void M() { void M() { - for (int i = 0; i < 10; ++i) + for (int i = 0; i < 10; i++) { } } diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index 3c6ec77d4ff6d..12e44ec864d75 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -81,12 +81,12 @@ protected override Task FixAllAsync( if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Increment)) { - return Increment((TExpressionSyntax)leftOfAssign.WithTriviaFrom(currentAssignment), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); + return IncrementOrDecrement(Increment, currentAssignment, leftOfAssign); } if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Decrement)) { - return Decrement((TExpressionSyntax)leftOfAssign.WithTriviaFrom(currentAssignment), postfix: syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)); + return IncrementOrDecrement(Decrement, currentAssignment, leftOfAssign); } var assignmentOpKind = _binaryToAssignmentMap[syntaxKinds.Convert(rightOfAssign.RawKind)]; @@ -100,6 +100,20 @@ protected override Task FixAllAsync( } return Task.CompletedTask; + + SyntaxNode IncrementOrDecrement(Func incrementOrDecrementFactory, SyntaxNode currentAssignment, SyntaxNode leftOfAssignment) + { + var postfix = syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent); + if (!postfix) + { + syntaxFacts.GetPartsOfForStatement(currentAssignment.Parent, out _, out _, out _, out var increments); + if (increments.Contains(currentAssignment)) + { + postfix = true; + } + } + return incrementOrDecrementFactory((TExpressionSyntax)leftOfAssignment.WithTriviaFrom(currentAssignment), postfix); + } } private class MyCodeAction : CustomCodeActions.DocumentChangeAction diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 11e1bd60da9c8..4dd780122a89a 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1453,6 +1453,21 @@ public SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node) public bool IsExpressionOfForeach([NotNullWhen(true)] SyntaxNode? node) => node?.Parent is ForEachStatementSyntax foreachStatement && foreachStatement.Expression == node; + public void GetPartsOfForStatement(SyntaxNode? node, out SyntaxNode? variableDeclaration, out SyntaxNode? startvalue, out SyntaxNode? endCondition, out ImmutableArray increments) + { + variableDeclaration = null; + startvalue = null; + endCondition = null; + increments = ImmutableArray.Empty; + + if (node is ForStatementSyntax forStatement) + { + variableDeclaration = forStatement.Declaration; + endCondition = forStatement.Condition; + increments = forStatement.Incrementors.ToImmutableArray(); + } + } + public SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node) => ((ExpressionStatementSyntax)node).Expression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index e20d223b20a6e..313c23a21b74b 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -143,6 +143,15 @@ internal interface ISyntaxFacts bool IsExpressionOfAwaitExpression([NotNullWhen(true)] SyntaxNode? node); SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node); bool IsExpressionOfForeach([NotNullWhen(true)] SyntaxNode? node); + /// + /// Returns the parts of a for loop. C#: for(var i = 0; i < 10; i++) VB: For i = 0 To 10 Step 1 + /// + /// The for loop statement + /// C#: var i = 0 | VB: i + /// C#: null | VB: 0 + /// C#: i < 10 | VB: 10 + /// C#: i++ | VB: Step 1 + void GetPartsOfForStatement(SyntaxNode? node, out SyntaxNode? variableDeclaration, out SyntaxNode? startValue, out SyntaxNode? endCondition, out ImmutableArray increments); void GetPartsOfTupleExpression(SyntaxNode node, out SyntaxToken openParen, out SeparatedSyntaxList arguments, out SyntaxToken closeParen) where TArgumentSyntax : SyntaxNode; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 9a6dfb6265fca..57bb4ecc6635c 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1515,6 +1515,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Return node IsNot Nothing AndAlso TryCast(node.Parent, ForEachStatementSyntax)?.Expression Is node End Function + Public Sub GetPartsOfForStatement(node As SyntaxNode, ByRef variableDeclaration As SyntaxNode, ByRef startValue As SyntaxNode, ByRef endCondition As SyntaxNode, ByRef increments As ImmutableArray(Of SyntaxNode)) Implements ISyntaxFacts.GetPartsOfForStatement + If TypeOf node Is ForStatementSyntax Then + Dim forStatement = DirectCast(node, ForStatementSyntax) + variableDeclaration = forStatement.ControlVariable + startValue = forStatement.FromValue + endCondition = forStatement.ToValue + increments = ImmutableArray.Create(Of SyntaxNode)(forStatement.StepClause) + End If + End Sub + Public Function GetExpressionOfExpressionStatement(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetExpressionOfExpressionStatement Return DirectCast(node, ExpressionStatementSyntax).Expression End Function From e7810a7d299d46502c64c61e6079718c914241c9 Mon Sep 17 00:00:00 2001 From: Martin Strecker Date: Wed, 23 Jun 2021 20:42:13 +0100 Subject: [PATCH 10/19] Move trivia handling. --- .../AbstractUseCompoundAssignmentCodeFixProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index 12e44ec864d75..4f4e52da3e639 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -112,7 +112,7 @@ SyntaxNode IncrementOrDecrement(Func postfix = true; } } - return incrementOrDecrementFactory((TExpressionSyntax)leftOfAssignment.WithTriviaFrom(currentAssignment), postfix); + return incrementOrDecrementFactory((TExpressionSyntax)leftOfAssignment, postfix).WithTriviaFrom(currentAssignment); } } From 34d7d9b40d64ed4def1583a113d784a84156ff3a Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 12:31:11 -0700 Subject: [PATCH 11/19] Use TimeSpan instead of int for durations --- .../Workspaces/ProjectCacheServiceFactory.cs | 4 ++-- .../ProjectCacheHostServiceFactoryTests.cs | 12 +++++----- .../GlobalOperationAwareIdleProcessor.cs | 4 ++-- .../Portable/SolutionCrawler/IdleProcessor.cs | 22 +++++++++---------- ...rkCoordinator.AbstractPriorityProcessor.cs | 4 ++-- .../WorkCoordinator.HighPriorityProcessor.cs | 4 ++-- ...oordinator.IncrementalAnalyzerProcessor.cs | 12 +++++----- .../WorkCoordinator.LowPriorityProcessor.cs | 4 ++-- ...WorkCoordinator.NormalPriorityProcessor.cs | 4 ++-- ...WorkCoordinator.SemanticChangeProcessor.cs | 12 +++++----- .../SolutionCrawler/WorkCoordinator.cs | 14 ++++++------ .../ProjectCacheService.SimpleMRUCache.cs | 6 ++--- .../Portable/Workspace/ProjectCacheService.cs | 2 +- ...ualStudioProjectCacheHostServiceFactory.cs | 6 ++--- .../Remote/Core/SolutionChecksumUpdater.cs | 3 ++- .../Host/ProjectCacheHostServiceFactory.cs | 4 ++-- .../RemoteHostService.PerformanceReporter.cs | 2 +- 17 files changed, 60 insertions(+), 59 deletions(-) diff --git a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs index aeff2305ff6a4..dfce03777c761 100644 --- a/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs +++ b/src/EditorFeatures/Core/Implementation/Workspaces/ProjectCacheServiceFactory.cs @@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces [Shared] internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan s_implicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -30,7 +30,7 @@ public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) return new ProjectCacheService(workspaceServices.Workspace); } - var service = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + var service = new ProjectCacheService(workspaceServices.Workspace, s_implicitCacheTimeout); // Also clear the cache when the solution is cleared or removed. workspaceServices.Workspace.WorkspaceChanged += (s, e) => diff --git a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs index a01bff35edaa1..1f55e9d7d2332 100644 --- a/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs +++ b/src/EditorFeatures/Test/Workspaces/ProjectCacheHostServiceFactoryTests.cs @@ -30,7 +30,7 @@ private static void Test(Action new object()); @@ -113,7 +113,7 @@ public void TestCacheDoesNotKeepObjectsAliveAfterOwnerIsCollected2() public void TestImplicitCacheKeepsObjectAlive1() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cacheService = new ProjectCacheService(workspace, int.MaxValue); + var cacheService = new ProjectCacheService(workspace, TimeSpan.MaxValue); var reference = ObjectReference.CreateFromFactory(() => new object()); reference.UseReference(r => cacheService.CacheObjectIfCachingEnabledForKey(ProjectId.CreateNewId(), (object)null, r)); reference.AssertHeld(); @@ -125,7 +125,7 @@ public void TestImplicitCacheKeepsObjectAlive1() public void TestImplicitCacheMonitoring() { var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cacheService = new ProjectCacheService(workspace, 10); + var cacheService = new ProjectCacheService(workspace, TimeSpan.FromMilliseconds(10)); var weak = PutObjectInImplicitCache(cacheService); weak.AssertReleased(); @@ -156,7 +156,7 @@ public void TestP2PReference() var instanceTracker = ObjectReference.CreateFromFactory(() => new object()); - var cacheService = new ProjectCacheService(workspace, int.MaxValue); + var cacheService = new ProjectCacheService(workspace, TimeSpan.MaxValue); using (var cache = cacheService.EnableCaching(project2.Id)) { instanceTracker.UseReference(r => cacheService.CacheObjectIfCachingEnabledForKey(project1.Id, (object)null, r)); @@ -184,7 +184,7 @@ public void TestEjectFromImplicitCache() var weakLast = ObjectReference.Create(compilations[compilations.Count - 1]); var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cache = new ProjectCacheService(workspace, int.MaxValue); + var cache = new ProjectCacheService(workspace, TimeSpan.MaxValue); for (var i = 0; i < ProjectCacheService.ImplicitCacheSize + 1; i++) { cache.CacheObjectIfCachingEnabledForKey(ProjectId.CreateNewId(), (object)null, compilations[i]); @@ -212,7 +212,7 @@ public void TestCacheCompilationTwice() var weak1 = ObjectReference.Create(comp1); var workspace = new AdhocWorkspace(MockHostServices.Instance, workspaceKind: WorkspaceKind.Host); - var cache = new ProjectCacheService(workspace, int.MaxValue); + var cache = new ProjectCacheService(workspace, TimeSpan.MaxValue); var key = ProjectId.CreateNewId(); var owner = new object(); cache.CacheObjectIfCachingEnabledForKey(key, owner, comp1); diff --git a/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs index 99a212faeb0a3..a87224e638ec3 100644 --- a/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/GlobalOperationAwareIdleProcessor.cs @@ -21,9 +21,9 @@ internal abstract class GlobalOperationAwareIdleProcessor : IdleProcessor public GlobalOperationAwareIdleProcessor( IAsynchronousOperationListener listener, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, backOffTimeSpanInMs, shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) { _globalOperation = null; _globalOperationTask = Task.CompletedTask; diff --git a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs index 5eb3bc29f5447..ff96d9124b6ae 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs @@ -12,28 +12,28 @@ namespace Microsoft.CodeAnalysis.SolutionCrawler { internal abstract class IdleProcessor { - private const int MinimumDelayInMS = 50; + private static readonly TimeSpan s_minimumDelay = TimeSpan.FromMilliseconds(50); protected readonly IAsynchronousOperationListener Listener; protected readonly CancellationToken CancellationToken; - protected readonly int BackOffTimeSpanInMS; + protected readonly TimeSpan BackOffTimeSpan; // points to processor task private Task? _processorTask; // there is one thread that writes to it and one thread reads from it - private int _lastAccessTimeInMS; + private SharedStopwatch _timeSinceLastAccess; public IdleProcessor( IAsynchronousOperationListener listener, - int backOffTimeSpanInMS, + TimeSpan backOffTimeSpan, CancellationToken cancellationToken) { Listener = listener; CancellationToken = cancellationToken; - BackOffTimeSpanInMS = backOffTimeSpanInMS; - _lastAccessTimeInMS = Environment.TickCount; + BackOffTimeSpan = backOffTimeSpan; + _timeSinceLastAccess = SharedStopwatch.StartNew(); } protected abstract Task WaitAsync(CancellationToken cancellationToken); @@ -46,7 +46,7 @@ protected void Start() } protected void UpdateLastAccessTime() - => _lastAccessTimeInMS = Environment.TickCount; + => _timeSinceLastAccess = SharedStopwatch.StartNew(); protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySource) { @@ -57,15 +57,15 @@ protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySo return; } - var diffInMS = Environment.TickCount - _lastAccessTimeInMS; - if (diffInMS >= BackOffTimeSpanInMS) + var diff = _timeSinceLastAccess.Elapsed; + if (diff >= BackOffTimeSpan) { return; } // TODO: will safestart/unwarp capture cancellation exception? - var timeLeft = BackOffTimeSpanInMS - diffInMS; - if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(MinimumDelayInMS, timeLeft)), CancellationToken).ConfigureAwait(false)) + var timeLeft = BackOffTimeSpan - diff; + if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)), CancellationToken).ConfigureAwait(false)) { // The delay terminated early to accommodate a blocking operation. Make sure to delay long // enough that low priority (on idle) operations get a chance to be triggered. diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs index 10f16c389c9cd..d84446da3ba92 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.AbstractPriorityProcessor.cs @@ -30,9 +30,9 @@ public AbstractPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _gate = new object(); _lazyAnalyzers = lazyAnalyzers; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs index b857367086186..33fe20c27b821 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.HighPriorityProcessor.cs @@ -35,9 +35,9 @@ public HighPriorityProcessor( IAsynchronousOperationListener listener, IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, backOffTimeSpanInMs, shutdownToken) + : base(listener, backOffTimeSpan, shutdownToken) { _processor = processor; _lazyAnalyzers = lazyAnalyzers; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs index 0b5f7d08d5f4a..bb5892d87acfb 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.IncrementalAnalyzerProcessor.cs @@ -50,9 +50,9 @@ public IncrementalAnalyzerProcessor( IEnumerable> analyzerProviders, bool initializeLazily, Registration registration, - int highBackOffTimeSpanInMs, - int normalBackOffTimeSpanInMs, - int lowBackOffTimeSpanInMs, + TimeSpan highBackOffTimeSpan, + TimeSpan normalBackOffTimeSpan, + TimeSpan lowBackOffTimeSpan, CancellationToken shutdownToken) { _logAggregator = new LogAggregator(); @@ -81,9 +81,9 @@ public IncrementalAnalyzerProcessor( var globalNotificationService = _registration.Workspace.Services.GetRequiredService(); - _highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpanInMs, shutdownToken); - _normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpanInMs, shutdownToken); - _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpanInMs, shutdownToken); + _highPriorityProcessor = new HighPriorityProcessor(listener, this, lazyActiveFileAnalyzers, highBackOffTimeSpan, shutdownToken); + _normalPriorityProcessor = new NormalPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, normalBackOffTimeSpan, shutdownToken); + _lowPriorityProcessor = new LowPriorityProcessor(listener, this, lazyAllAnalyzers, globalNotificationService, lowBackOffTimeSpan, shutdownToken); } private static IDiagnosticAnalyzerService? GetDiagnosticAnalyzerService(IEnumerable> analyzerProviders) diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs index 19b00ffb7a35b..de3db50989511 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.LowPriorityProcessor.cs @@ -30,9 +30,9 @@ public LowPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _workItemQueue = new AsyncProjectWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs index 213ac6b540b6f..681e3ae8c5d49 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.NormalPriorityProcessor.cs @@ -50,9 +50,9 @@ public NormalPriorityProcessor( IncrementalAnalyzerProcessor processor, Lazy> lazyAnalyzers, IGlobalOperationNotificationService globalOperationNotificationService, - int backOffTimeSpanInMs, + TimeSpan backOffTimeSpan, CancellationToken shutdownToken) - : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpanInMs, shutdownToken) + : base(listener, processor, lazyAnalyzers, globalOperationNotificationService, backOffTimeSpan, shutdownToken) { _running = Task.CompletedTask; _workItemQueue = new AsyncDocumentWorkItemQueue(processor._registration.ProgressReporter, processor._registration.Workspace); diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs index b5cf17f1660fd..25bdd62a21fe3 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.SemanticChangeProcessor.cs @@ -41,16 +41,16 @@ public SemanticChangeProcessor( IAsynchronousOperationListener listener, Registration registration, IncrementalAnalyzerProcessor documentWorkerProcessor, - int backOffTimeSpanInMS, - int projectBackOffTimeSpanInMS, + TimeSpan backOffTimeSpan, + TimeSpan projectBackOffTimeSpan, CancellationToken cancellationToken) - : base(listener, backOffTimeSpanInMS, cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) { _gate = new SemaphoreSlim(initialCount: 0); _registration = registration; - _processor = new ProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpanInMS, cancellationToken); + _processor = new ProjectProcessor(listener, registration, documentWorkerProcessor, projectBackOffTimeSpan, cancellationToken); _workGate = new NonReentrantLock(); _pendingWork = new Dictionary(); @@ -352,9 +352,9 @@ public ProjectProcessor( IAsynchronousOperationListener listener, Registration registration, IncrementalAnalyzerProcessor processor, - int backOffTimeSpanInMS, + TimeSpan backOffTimeSpan, CancellationToken cancellationToken) - : base(listener, backOffTimeSpanInMS, cancellationToken) + : base(listener, backOffTimeSpan, cancellationToken) { _registration = registration; _processor = processor; diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 7cf786abe40fe..51bdc2490fb4e 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -57,18 +57,18 @@ public WorkCoordinator( _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); - var activeFileBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS); - var allFilesWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS); - var entireProjectWorkerBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS); + var activeFileBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS)); + var allFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS)); + var entireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS)); _documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor( listener, analyzerProviders, initializeLazily, _registration, - activeFileBackOffTimeSpanInMS, allFilesWorkerBackOffTimeSpanInMS, entireProjectWorkerBackOffTimeSpanInMS, _shutdownToken); + activeFileBackOffTimeSpan, allFilesWorkerBackOffTimeSpan, entireProjectWorkerBackOffTimeSpan, _shutdownToken); - var semanticBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS); - var projectBackOffTimeSpanInMS = _optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS); + var semanticBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS)); + var projectBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS)); - _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpanInMS, projectBackOffTimeSpanInMS, _shutdownToken); + _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); // if option is on if (_optionService.GetOption(InternalSolutionCrawlerOptions.SolutionCrawler)) diff --git a/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs index 0121f4f09e907..ed90a6a80e861 100644 --- a/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.SimpleMRUCache.cs @@ -93,9 +93,9 @@ private class ImplicitCacheMonitor : IdleProcessor private readonly ProjectCacheService _owner; private readonly SemaphoreSlim _gate; - public ImplicitCacheMonitor(ProjectCacheService owner, int backOffTimeSpanInMS) + public ImplicitCacheMonitor(ProjectCacheService owner, TimeSpan backOffTimeSpan) : base(AsynchronousOperationListenerProvider.NullListener, - backOffTimeSpanInMS, + backOffTimeSpan, CancellationToken.None) { _owner = owner; @@ -106,7 +106,7 @@ public ImplicitCacheMonitor(ProjectCacheService owner, int backOffTimeSpanInMS) protected override Task ExecuteAsync() { - _owner.ClearExpiredImplicitCache(DateTime.UtcNow - TimeSpan.FromMilliseconds(BackOffTimeSpanInMS)); + _owner.ClearExpiredImplicitCache(DateTime.UtcNow - BackOffTimeSpan); return Task.CompletedTask; } diff --git a/src/Features/Core/Portable/Workspace/ProjectCacheService.cs b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs index 555fffd75022e..dffbc15cc5c51 100644 --- a/src/Features/Core/Portable/Workspace/ProjectCacheService.cs +++ b/src/Features/Core/Portable/Workspace/ProjectCacheService.cs @@ -34,7 +34,7 @@ internal partial class ProjectCacheService : IProjectCacheHostService public ProjectCacheService(Workspace workspace) => _workspace = workspace; - public ProjectCacheService(Workspace workspace, int implicitCacheTimeout) + public ProjectCacheService(Workspace workspace, TimeSpan implicitCacheTimeout) { _workspace = workspace; diff --git a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs index 23b3b6b1a8f8e..3d9a0808a469d 100644 --- a/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs +++ b/src/VisualStudio/Core/Def/Implementation/Workspace/VisualStudioProjectCacheHostServiceFactory.cs @@ -16,7 +16,7 @@ namespace Microsoft.CodeAnalysis.Editor.Implementation.Workspaces [Shared] internal partial class VisualStudioProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan ImplicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -42,7 +42,7 @@ private static IWorkspaceService GetMiscProjectCache(HostWorkspaceServices works return new ProjectCacheService(workspaceServices.Workspace); } - var projectCacheService = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + var projectCacheService = new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeout); // Also clear the cache when the solution is cleared or removed. workspaceServices.Workspace.WorkspaceChanged += (s, e) => @@ -59,7 +59,7 @@ private static IWorkspaceService GetMiscProjectCache(HostWorkspaceServices works private static IWorkspaceService GetVisualStudioProjectCache(HostWorkspaceServices workspaceServices) { // We will finish setting this up in VisualStudioWorkspaceImpl.DeferredInitializationState - return new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + return new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeout); } internal static void ConnectProjectCacheServiceToDocumentTracking(HostWorkspaceServices workspaceServices, ProjectCacheService projectCacheService) diff --git a/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs b/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs index 0878c25167aac..447b49f1803e2 100644 --- a/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs +++ b/src/Workspaces/Remote/Core/SolutionChecksumUpdater.cs @@ -4,6 +4,7 @@ #nullable disable +using System; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Internal.Log; @@ -34,7 +35,7 @@ internal sealed class SolutionChecksumUpdater : GlobalOperationAwareIdleProcesso public SolutionChecksumUpdater(Workspace workspace, IAsynchronousOperationListenerProvider listenerProvider, CancellationToken shutdownToken) : base(listenerProvider.GetListener(FeatureAttribute.SolutionChecksumUpdater), workspace.Services.GetService(), - workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS), shutdownToken) + TimeSpan.FromMilliseconds(workspace.Options.GetOption(RemoteHostOptions.SolutionChecksumMonitorBackOffTimeSpanInMS)), shutdownToken) { _workspace = workspace; _textChangeQueue = new TaskQueue(Listener, TaskScheduler.Default); diff --git a/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs b/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs index c53e0538403c1..0d58c936114b2 100644 --- a/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs +++ b/src/Workspaces/Remote/ServiceHub/Host/ProjectCacheHostServiceFactory.cs @@ -13,7 +13,7 @@ namespace Microsoft.CodeAnalysis.Remote [Shared] internal partial class ProjectCacheHostServiceFactory : IWorkspaceServiceFactory { - private const int ImplicitCacheTimeoutInMS = 10000; + private static readonly TimeSpan s_implicitCacheTimeout = TimeSpan.FromMilliseconds(10000); [ImportingConstructor] [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] @@ -22,6 +22,6 @@ public ProjectCacheHostServiceFactory() } public IWorkspaceService CreateService(HostWorkspaceServices workspaceServices) - => new ProjectCacheService(workspaceServices.Workspace, ImplicitCacheTimeoutInMS); + => new ProjectCacheService(workspaceServices.Workspace, s_implicitCacheTimeout); } } diff --git a/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs b/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs index deaa218aec9c3..e6e77c694d198 100644 --- a/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs +++ b/src/Workspaces/Remote/ServiceHub/Services/Host/RemoteHostService.PerformanceReporter.cs @@ -43,7 +43,7 @@ public PerformanceReporter( : base( AsynchronousOperationListenerProvider.NullListener, globalOperationNotificationService, - (int)reportingInterval.TotalMilliseconds, + reportingInterval, shutdownToken) { _event = new SemaphoreSlim(initialCount: 0); From 1ac79a4a982d11d15833ab8e1d282acbe7315510 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 23 Jun 2021 13:50:34 -0700 Subject: [PATCH 12/19] Simplify --- ...arpUseCompoundAssignmentCodeFixProvider.cs | 13 +++++++ ...actUseCompoundAssignmentCodeFixProvider.cs | 36 +++++++++---------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs index 3ab74e9b9b6fc..a9b93d04d5c2e 100644 --- a/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/CSharp/CodeFixes/UseCompoundAssignment/CSharpUseCompoundAssignmentCodeFixProvider.cs @@ -6,6 +6,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.UseCompoundAssignment; namespace Microsoft.CodeAnalysis.CSharp.UseCompoundAssignment @@ -45,5 +46,17 @@ private static ExpressionSyntax Postfix(SyntaxKind kind, ExpressionSyntax operan private static ExpressionSyntax Prefix(SyntaxKind kind, ExpressionSyntax operand) => SyntaxFactory.PrefixUnaryExpression(kind, operand); + + protected override bool PreferPostfix(ISyntaxFactsService syntaxFacts, AssignmentExpressionSyntax currentAssignment) + { + // in `for (...; x = x + 1)` we prefer to translate that idiomatically as `for (...; x++)` + if (currentAssignment.Parent is ForStatementSyntax forStatement && + forStatement.Incrementors.Contains(currentAssignment)) + { + return true; + } + + return base.PreferPostfix(syntaxFacts, currentAssignment); + } } } diff --git a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs index 4f4e52da3e639..9ef813bac5d2d 100644 --- a/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs +++ b/src/Analyzers/Core/CodeFixes/UseCompoundAssignment/AbstractUseCompoundAssignmentCodeFixProvider.cs @@ -68,8 +68,11 @@ protected override Task FixAllAsync( var assignment = diagnostic.AdditionalLocations[0].FindNode(getInnermostNodeForTie: true, cancellationToken); editor.ReplaceNode(assignment, - (currentAssignment, generator) => + (current, generator) => { + if (current is not TAssignmentSyntax currentAssignment) + return current; + syntaxFacts.GetPartsOfAssignmentExpressionOrStatement(currentAssignment, out var leftOfAssign, out var equalsToken, out var rightOfAssign); @@ -80,14 +83,10 @@ protected override Task FixAllAsync( out _, out var opToken, out var rightExpr); if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Increment)) - { - return IncrementOrDecrement(Increment, currentAssignment, leftOfAssign); - } + return Increment((TExpressionSyntax)leftOfAssign, PreferPostfix(syntaxFacts, currentAssignment)).WithTriviaFrom(currentAssignment); if (diagnostic.Properties.ContainsKey(UseCompoundAssignmentUtilities.Decrement)) - { - return IncrementOrDecrement(Decrement, currentAssignment, leftOfAssign); - } + return Decrement((TExpressionSyntax)leftOfAssign, PreferPostfix(syntaxFacts, currentAssignment)).WithTriviaFrom(currentAssignment); var assignmentOpKind = _binaryToAssignmentMap[syntaxKinds.Convert(rightOfAssign.RawKind)]; var compoundOperator = Token(_assignmentToTokenMap[assignmentOpKind]); @@ -100,20 +99,17 @@ protected override Task FixAllAsync( } return Task.CompletedTask; + } - SyntaxNode IncrementOrDecrement(Func incrementOrDecrementFactory, SyntaxNode currentAssignment, SyntaxNode leftOfAssignment) - { - var postfix = syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent); - if (!postfix) - { - syntaxFacts.GetPartsOfForStatement(currentAssignment.Parent, out _, out _, out _, out var increments); - if (increments.Contains(currentAssignment)) - { - postfix = true; - } - } - return incrementOrDecrementFactory((TExpressionSyntax)leftOfAssignment, postfix).WithTriviaFrom(currentAssignment); - } + protected virtual bool PreferPostfix(ISyntaxFactsService syntaxFacts, TAssignmentSyntax currentAssignment) + { + // If we have `x = x + 1;` on it's own, then we prefer `x++` as idiomatic. + if (syntaxFacts.IsSimpleAssignmentStatement(currentAssignment.Parent)) + return true; + + // In any other circumstance, the value of the assignment might be read, so we need to transform to + // ++x to ensure that we preserve semantics. + return false; } private class MyCodeAction : CustomCodeActions.DocumentChangeAction From ecd8a44c291449b37681fc68f961ffa02313d4e2 Mon Sep 17 00:00:00 2001 From: Cyrus Najmabadi Date: Wed, 23 Jun 2021 13:51:48 -0700 Subject: [PATCH 13/19] remove syntax facts --- .../Services/SyntaxFacts/CSharpSyntaxFacts.cs | 15 --------------- .../Core/Services/SyntaxFacts/ISyntaxFacts.cs | 9 --------- .../SyntaxFacts/VisualBasicSyntaxFacts.vb | 10 ---------- 3 files changed, 34 deletions(-) diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs index 4dd780122a89a..11e1bd60da9c8 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/CSharp/Services/SyntaxFacts/CSharpSyntaxFacts.cs @@ -1453,21 +1453,6 @@ public SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node) public bool IsExpressionOfForeach([NotNullWhen(true)] SyntaxNode? node) => node?.Parent is ForEachStatementSyntax foreachStatement && foreachStatement.Expression == node; - public void GetPartsOfForStatement(SyntaxNode? node, out SyntaxNode? variableDeclaration, out SyntaxNode? startvalue, out SyntaxNode? endCondition, out ImmutableArray increments) - { - variableDeclaration = null; - startvalue = null; - endCondition = null; - increments = ImmutableArray.Empty; - - if (node is ForStatementSyntax forStatement) - { - variableDeclaration = forStatement.Declaration; - endCondition = forStatement.Condition; - increments = forStatement.Incrementors.ToImmutableArray(); - } - } - public SyntaxNode GetExpressionOfExpressionStatement(SyntaxNode node) => ((ExpressionStatementSyntax)node).Expression; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs index 313c23a21b74b..e20d223b20a6e 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Services/SyntaxFacts/ISyntaxFacts.cs @@ -143,15 +143,6 @@ internal interface ISyntaxFacts bool IsExpressionOfAwaitExpression([NotNullWhen(true)] SyntaxNode? node); SyntaxNode GetExpressionOfAwaitExpression(SyntaxNode node); bool IsExpressionOfForeach([NotNullWhen(true)] SyntaxNode? node); - /// - /// Returns the parts of a for loop. C#: for(var i = 0; i < 10; i++) VB: For i = 0 To 10 Step 1 - /// - /// The for loop statement - /// C#: var i = 0 | VB: i - /// C#: null | VB: 0 - /// C#: i < 10 | VB: 10 - /// C#: i++ | VB: Step 1 - void GetPartsOfForStatement(SyntaxNode? node, out SyntaxNode? variableDeclaration, out SyntaxNode? startValue, out SyntaxNode? endCondition, out ImmutableArray increments); void GetPartsOfTupleExpression(SyntaxNode node, out SyntaxToken openParen, out SeparatedSyntaxList arguments, out SyntaxToken closeParen) where TArgumentSyntax : SyntaxNode; diff --git a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb index 57bb4ecc6635c..9a6dfb6265fca 100644 --- a/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb +++ b/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/VisualBasic/Services/SyntaxFacts/VisualBasicSyntaxFacts.vb @@ -1515,16 +1515,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices Return node IsNot Nothing AndAlso TryCast(node.Parent, ForEachStatementSyntax)?.Expression Is node End Function - Public Sub GetPartsOfForStatement(node As SyntaxNode, ByRef variableDeclaration As SyntaxNode, ByRef startValue As SyntaxNode, ByRef endCondition As SyntaxNode, ByRef increments As ImmutableArray(Of SyntaxNode)) Implements ISyntaxFacts.GetPartsOfForStatement - If TypeOf node Is ForStatementSyntax Then - Dim forStatement = DirectCast(node, ForStatementSyntax) - variableDeclaration = forStatement.ControlVariable - startValue = forStatement.FromValue - endCondition = forStatement.ToValue - increments = ImmutableArray.Create(Of SyntaxNode)(forStatement.StepClause) - End If - End Sub - Public Function GetExpressionOfExpressionStatement(node As SyntaxNode) As SyntaxNode Implements ISyntaxFacts.GetExpressionOfExpressionStatement Return DirectCast(node, ExpressionStatementSyntax).Expression End Function From 6e2ce53655c62e4eb05209226e30382a81026d23 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:14:39 -0700 Subject: [PATCH 14/19] Expedite waits in WorkCoordinatorTests --- .../SolutionCrawler/WorkCoordinatorTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 14eb9c357849b..87b75b5d78cc7 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -788,14 +788,23 @@ internal async Task Document_Cancellation(BackgroundAnalysisScope analysisScope, var expectedDocumentSyntaxEvents = 1; var expectedDocumentSemanticEvents = 5; + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) { analyzer.RunningEvent.Wait(); } + token.Dispose(); + workspace.ChangeDocument(document.Id, SourceText.From("// ")); await WaitAsync(service, workspace); + await expeditedWait; service.Unregister(workspace); @@ -833,6 +842,12 @@ internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope service.Register(workspace); + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + workspace.ChangeDocument(document.Id, SourceText.From("//")); if (expectedDocumentSyntaxEvents > 0 || expectedDocumentSemanticEvents > 0) { @@ -846,8 +861,11 @@ internal async Task Document_Cancellation_MultipleTimes(BackgroundAnalysisScope analyzer.RunningEvent.Wait(); } + token.Dispose(); + workspace.ChangeDocument(document.Id, SourceText.From("// ")); await WaitAsync(service, workspace); + await expeditedWait; service.Unregister(workspace); @@ -1273,6 +1291,12 @@ public async Task FileFromSameProjectTogetherTest() await WaitWaiterAsync(workspace.ExportProvider); + var listenerProvider = GetListenerProvider(workspace.ExportProvider); + + // start an operation that allows an expedited wait to cover the remainder of the delayed operations in the test + var token = listenerProvider.GetListener(FeatureAttribute.SolutionCrawler).BeginAsyncOperation("Test operation"); + var expeditedWait = listenerProvider.GetWaiter(FeatureAttribute.SolutionCrawler).ExpeditedWaitAsync(); + // we want to test order items processed by solution crawler. // but since everything async, lazy and cancellable, order is not 100% deterministic. an item might // start to be processed, and get cancelled due to newly enqueued item requiring current work to be re-processed @@ -1319,8 +1343,11 @@ await crawlerListener.WaitUntilConditionIsMetAsync( operation.Done(); } + token.Dispose(); + // wait analyzers to finish process await WaitAsync(service, workspace); + await expeditedWait; Assert.Equal(1, worker.DocumentIds.Take(5).Select(d => d.ProjectId).Distinct().Count()); Assert.Equal(1, worker.DocumentIds.Skip(5).Take(5).Select(d => d.ProjectId).Distinct().Count()); From 089ff931895f76a324e839296115d222f07869c9 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:32:59 -0700 Subject: [PATCH 15/19] Remove unnecessary configurable delays --- ...eviewSolutionCrawlerRegistrationService.cs | 4 ++-- .../SolutionCrawler/WorkCoordinatorTests.cs | 15 ------------ .../InternalSolutionCrawlerOptions.cs | 24 ++++++------------- .../InternalSolutionCrawlerOptionsProvider.cs | 7 +----- .../SolutionCrawler/WorkCoordinator.cs | 10 ++++---- .../Impl/CodeModel/ProjectCodeModelFactory.cs | 5 ++-- 6 files changed, 17 insertions(+), 48 deletions(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs index 0b5cf858ab7e5..a198812083642 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs @@ -72,7 +72,7 @@ public void Register(Workspace workspace) private async Task AnalyzeAsync() { - var workerBackOffTimeSpanInMS = _workspace.Options.GetOption(InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS); + var workerBackOffTimeSpan = InternalSolutionCrawlerOptions.PreviewBackOffTimeSpan; var incrementalAnalyzer = _owner._analyzerService.CreateIncrementalAnalyzer(_workspace); var solution = _workspace.CurrentSolution; @@ -89,7 +89,7 @@ private async Task AnalyzeAsync() } // delay analyzing - await Task.Delay(workerBackOffTimeSpanInMS, _source.Token).ConfigureAwait(false); + await Task.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); // do actual analysis if (textDocument is Document document) diff --git a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs index 87b75b5d78cc7..3ecd69d10d544 100644 --- a/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs +++ b/src/EditorFeatures/Test/SolutionCrawler/WorkCoordinatorTests.cs @@ -1366,7 +1366,6 @@ private static async Task InsertText(string code, string text, bool expectDocume composition: EditorTestCompositions.EditorFeatures.AddExcludedPartTypes(typeof(IIncrementalAnalyzerProvider)).AddParts(typeof(AnalyzerProviderNoWaitNoBlock)), workspaceKind: SolutionCrawlerWorkspaceKind); - SetOptions(workspace); var testDocument = workspace.Documents.First(); var textBuffer = testDocument.GetTextBuffer(); @@ -1503,18 +1502,6 @@ private static IEnumerable GetDocuments(ProjectId projectId, int c private static AsynchronousOperationListenerProvider GetListenerProvider(ExportProvider provider) => provider.GetExportedValue(); - private static void SetOptions(Workspace workspace) - { - // override default timespan to make test run faster - workspace.TryApplyChanges(workspace.CurrentSolution.WithOptions(workspace.Options - .WithChangedOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS, 0) - .WithChangedOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS, 100))); - } - private static void MakeFirstDocumentActive(Project project) => MakeDocumentActive(project.Documents.First()); @@ -1545,8 +1532,6 @@ public WorkCoordinatorWorkspace(string workspaceKind = null, bool disablePartial Assert.False(_workspaceWaiter.HasPendingWork); Assert.False(_solutionCrawlerWaiter.HasPendingWork); - - WorkCoordinatorTests.SetOptions(this); } public static WorkCoordinatorWorkspace CreateWithAnalysisScope(BackgroundAnalysisScope analysisScope, string workspaceKind = null, bool disablePartialSolutions = true, Type incrementalAnalyzer = null) diff --git a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs index b6eb290a7754d..58529ebc46875 100644 --- a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs +++ b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptions.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using Microsoft.CodeAnalysis.Options; namespace Microsoft.CodeAnalysis.SolutionCrawler @@ -16,22 +17,11 @@ internal static class InternalSolutionCrawlerOptions public static readonly Option2 DirectDependencyPropagationOnly = new(nameof(InternalSolutionCrawlerOptions), "Project propagation only on direct dependency", defaultValue: true, storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Project propagation only on direct dependency")); - public static readonly Option2 ActiveFileWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Active file worker backoff timespan in ms", defaultValue: 100, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Active file worker backoff timespan in ms")); - - public static readonly Option2 AllFilesWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "All files worker backoff timespan in ms", defaultValue: 1500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "All files worker backoff timespan in ms")); - - public static readonly Option2 EntireProjectWorkerBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Entire project analysis worker backoff timespan in ms", defaultValue: 5000, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Entire project analysis worker backoff timespan in ms")); - - public static readonly Option2 SemanticChangeBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Semantic change backoff timespan in ms", defaultValue: 100, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Semantic change backoff timespan in ms")); - - public static readonly Option2 ProjectPropagationBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Project propagation backoff timespan in ms", defaultValue: 500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Project propagation backoff timespan in ms")); - - public static readonly Option2 PreviewBackOffTimeSpanInMS = new(nameof(InternalSolutionCrawlerOptions), "Preview backoff timespan in ms", defaultValue: 500, - storageLocations: new LocalUserProfileStorageLocation(LocalRegistryPath + "Preview backoff timespan in ms")); + public static readonly TimeSpan ActiveFileWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan AllFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(1500); + public static readonly TimeSpan EntireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(5000); + public static readonly TimeSpan SemanticChangeBackOffTimeSpan = TimeSpan.FromMilliseconds(100); + public static readonly TimeSpan ProjectPropagationBackOffTimeSpan = TimeSpan.FromMilliseconds(500); + public static readonly TimeSpan PreviewBackOffTimeSpan = TimeSpan.FromMilliseconds(500); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs index 37ae86b2c73bc..2a5e8c14adad1 100644 --- a/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs +++ b/src/Features/Core/Portable/SolutionCrawler/InternalSolutionCrawlerOptionsProvider.cs @@ -22,11 +22,6 @@ public InternalSolutionCrawlerOptionsProvider() public ImmutableArray Options { get; } = ImmutableArray.Create( InternalSolutionCrawlerOptions.SolutionCrawler, - InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS, - InternalSolutionCrawlerOptions.PreviewBackOffTimeSpanInMS); + InternalSolutionCrawlerOptions.DirectDependencyPropagationOnly); } } diff --git a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs index 51bdc2490fb4e..430d5e1f23180 100644 --- a/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs +++ b/src/Features/Core/Portable/SolutionCrawler/WorkCoordinator.cs @@ -57,16 +57,16 @@ public WorkCoordinator( _eventProcessingQueue = new TaskQueue(listener, TaskScheduler.Default); - var activeFileBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpanInMS)); - var allFilesWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpanInMS)); - var entireProjectWorkerBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpanInMS)); + var activeFileBackOffTimeSpan = InternalSolutionCrawlerOptions.ActiveFileWorkerBackOffTimeSpan; + var allFilesWorkerBackOffTimeSpan = InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpan; + var entireProjectWorkerBackOffTimeSpan = InternalSolutionCrawlerOptions.EntireProjectWorkerBackOffTimeSpan; _documentAndProjectWorkerProcessor = new IncrementalAnalyzerProcessor( listener, analyzerProviders, initializeLazily, _registration, activeFileBackOffTimeSpan, allFilesWorkerBackOffTimeSpan, entireProjectWorkerBackOffTimeSpan, _shutdownToken); - var semanticBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpanInMS)); - var projectBackOffTimeSpan = TimeSpan.FromMilliseconds(_optionService.GetOption(InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpanInMS)); + var semanticBackOffTimeSpan = InternalSolutionCrawlerOptions.SemanticChangeBackOffTimeSpan; + var projectBackOffTimeSpan = InternalSolutionCrawlerOptions.ProjectPropagationBackOffTimeSpan; _semanticChangeProcessor = new SemanticChangeProcessor(listener, _registration, _documentAndProjectWorkerProcessor, semanticBackOffTimeSpan, projectBackOffTimeSpan, _shutdownToken); diff --git a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs index 77abdc8dd3ea8..06a6f7fc6a383 100644 --- a/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs +++ b/src/VisualStudio/Core/Impl/CodeModel/ProjectCodeModelFactory.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Editor.Shared.Utilities; using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.Shared.TestHooks; +using Microsoft.CodeAnalysis.SolutionCrawler; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Threading; using Roslyn.Utilities; @@ -27,8 +28,6 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel [Export(typeof(ProjectCodeModelFactory))] internal sealed class ProjectCodeModelFactory : ForegroundThreadAffinitizedObject, IProjectCodeModelFactory { - private static readonly TimeSpan s_documentBatchProcessingCadence = TimeSpan.FromMilliseconds(1500); - private readonly ConcurrentDictionary _projectCodeModels = new ConcurrentDictionary(); private readonly VisualStudioWorkspace _visualStudioWorkspace; @@ -57,7 +56,7 @@ public ProjectCodeModelFactory( // for the same documents. Once enough time has passed, take the documents that were changed and run // through them, firing their latest events. _documentsToFireEventsFor = new AsyncBatchingWorkQueue( - s_documentBatchProcessingCadence, + InternalSolutionCrawlerOptions.AllFilesWorkerBackOffTimeSpan, ProcessNextDocumentBatchAsync, // We only care about unique doc-ids, so pass in this comparer to collapse streams of changes for a // single document down to one notification. From df89ea01446e0c8bdbef80c5ea091ea14342b023 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:35:33 -0700 Subject: [PATCH 16/19] Use IExpeditableDelaySource for delay in PreviewSolutionCrawlerRegistrationServiceFactory --- .../Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs index a198812083642..ec124733a96ec 100644 --- a/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs +++ b/src/EditorFeatures/Core/Shared/Preview/PreviewSolutionCrawlerRegistrationService.cs @@ -89,7 +89,7 @@ private async Task AnalyzeAsync() } // delay analyzing - await Task.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); + await _owner._listener.Delay(workerBackOffTimeSpan, _source.Token).ConfigureAwait(false); // do actual analysis if (textDocument is Document document) From b4f21dacb50ef469ab53d2365e20d3f7b89039a4 Mon Sep 17 00:00:00 2001 From: Sam Harwell Date: Wed, 23 Jun 2021 14:37:16 -0700 Subject: [PATCH 17/19] Yield instead of delay on expedited wait completion in IdleProcessor --- .../Core/Portable/SolutionCrawler/IdleProcessor.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs index ff96d9124b6ae..84618890b7bef 100644 --- a/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs +++ b/src/Features/Core/Portable/SolutionCrawler/IdleProcessor.cs @@ -67,12 +67,12 @@ protected async Task WaitForIdleAsync(IExpeditableDelaySource expeditableDelaySo var timeLeft = BackOffTimeSpan - diff; if (!await expeditableDelaySource.Delay(TimeSpan.FromMilliseconds(Math.Max(s_minimumDelay.TotalMilliseconds, timeLeft.TotalMilliseconds)), CancellationToken).ConfigureAwait(false)) { - // The delay terminated early to accommodate a blocking operation. Make sure to delay long - // enough that low priority (on idle) operations get a chance to be triggered. + // The delay terminated early to accommodate a blocking operation. Make sure to yield so low + // priority (on idle) operations get a chance to be triggered. // - // 📝 At the time this was discovered, it was not clear exactly why the delay was needed in order - // to avoid live-lock scenarios. - await Task.Delay(TimeSpan.FromMilliseconds(10), CancellationToken).ConfigureAwait(false); + // 📝 At the time this was discovered, it was not clear exactly why the yield (previously delay) + // was needed in order to avoid live-lock scenarios. + await Task.Yield().ConfigureAwait(false); return; } } From f814914a4990d6b424e2ba1476da40b9832ada5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20Matou=C5=A1ek?= Date: Wed, 23 Jun 2021 15:33:04 -0700 Subject: [PATCH 18/19] Add UpdatedTypes to WatchHotReloadService.Update (#54340) --- .../EditAndContinueWorkspaceServiceTests.cs | 16 ++++++++++++++++ .../Core/Portable/EditAndContinue/EditSession.cs | 3 +-- .../Watch/Api/WatchHotReloadService.cs | 6 ++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index 6f9478481297d..e95bc192fb952 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -1905,6 +1905,7 @@ void ValidateDelta(ManagedModuleUpdate delta) Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(0x06000001, delta.UpdatedMethods.Single()); + Assert.Equal(0x02000002, delta.UpdatedTypes.Single()); Assert.Equal(moduleId, delta.Module); Assert.Empty(delta.ExceptionRegions); Assert.Empty(delta.SequencePoints); @@ -2042,6 +2043,7 @@ public async Task BreakMode_ValidSignificantChange_EmitSuccessful_UpdateDeferred Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(0x06000002, delta.UpdatedMethods.Single()); + Assert.Equal(0x02000002, delta.UpdatedTypes.Single()); Assert.Equal(moduleId, delta.Module); Assert.Empty(delta.ExceptionRegions); Assert.Empty(delta.SequencePoints); @@ -2157,6 +2159,7 @@ partial class C { int Y = 2; } Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(6, delta.UpdatedMethods.Length); // F, C.C(), D.D(), E.E(int), E.E(int, int), lambda + AssertEx.SetEqual(new[] { 0x02000002, 0x02000003, 0x02000004, 0x02000005 }, delta.UpdatedTypes, itemInspector: t => "0x" + t.ToString("X")); EndDebuggingSession(debuggingSession); } @@ -2271,6 +2274,7 @@ class C { int Y => 2; } Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(2, delta.UpdatedMethods.Length); + AssertEx.Equal(new[] { 0x02000002, 0x02000003 }, delta.UpdatedTypes, itemInspector: t => "0x" + t.ToString("X")); EndDebuggingSession(debuggingSession); } @@ -2332,6 +2336,7 @@ int M() Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Empty(delta.UpdatedMethods); + Assert.Empty(delta.UpdatedTypes); EndDebuggingSession(debuggingSession); } @@ -2375,6 +2380,7 @@ partial class C { int X = 1; } Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(1, delta.UpdatedMethods.Length); // constructor update + Assert.Equal(0x02000002, delta.UpdatedTypes.Single()); EndDebuggingSession(debuggingSession); } @@ -2421,6 +2427,7 @@ class C { int Y => 1; } Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(1, delta.UpdatedMethods.Length); + Assert.Equal(0x02000003, delta.UpdatedTypes.Single()); EndDebuggingSession(debuggingSession); } @@ -2463,6 +2470,7 @@ class C { int Y => 1; } Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(1, delta.UpdatedMethods.Length); + Assert.Equal(0x02000003, delta.UpdatedTypes.Single()); EndDebuggingSession(debuggingSession); } @@ -2502,6 +2510,7 @@ public async Task BreakMode_ValidSignificantChange_SourceGenerators_DocumentRemo Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Equal(1, delta.UpdatedMethods.Length); + Assert.Equal(0x02000002, delta.UpdatedTypes.Single()); EndDebuggingSession(debuggingSession); } @@ -3191,6 +3200,7 @@ void F() Assert.NotEmpty(delta.MetadataDelta); Assert.NotEmpty(delta.PdbDelta); Assert.Empty(delta.UpdatedMethods); + Assert.Empty(delta.UpdatedTypes); AssertEx.Equal(new[] { @@ -3260,6 +3270,7 @@ static void F() var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); Assert.Equal(0x06000003, updates.Updates.Single().UpdatedMethods.Single()); + Assert.Equal(0x02000002, updates.Updates.Single().UpdatedTypes.Single()); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); CommitSolutionUpdate(debuggingSession); @@ -3278,6 +3289,7 @@ static void F() (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); Assert.Equal(0x06000003, updates.Updates.Single().UpdatedMethods.Single()); + Assert.Equal(0x02000002, updates.Updates.Single().UpdatedTypes.Single()); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); CommitSolutionUpdate(debuggingSession); @@ -3305,6 +3317,7 @@ static void F() (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); Assert.Equal(0x06000003, updates.Updates.Single().UpdatedMethods.Single()); + Assert.Equal(0x02000002, updates.Updates.Single().UpdatedTypes.Single()); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); CommitSolutionUpdate(debuggingSession); @@ -3431,6 +3444,7 @@ static void F() var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); Assert.Equal(0x06000003, updates.Updates.Single().UpdatedMethods.Single()); + Assert.Equal(0x02000002, updates.Updates.Single().UpdatedTypes.Single()); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); CommitSolutionUpdate(debuggingSession); @@ -3489,6 +3503,7 @@ static void F() var (updates, emitDiagnostics) = await EmitSolutionUpdateAsync(debuggingSession, solution); Assert.Empty(emitDiagnostics); Assert.Equal(0x06000003, updates.Updates.Single().UpdatedMethods.Single()); + Assert.Equal(0x02000002, updates.Updates.Single().UpdatedTypes.Single()); Assert.Equal(ManagedModuleUpdateStatus.Ready, updates.Status); CommitSolutionUpdate(debuggingSession); @@ -3625,6 +3640,7 @@ public async Task WatchHotReloadServiceTest() var result = await hotReload.EmitSolutionUpdateAsync(solution, CancellationToken.None); Assert.Empty(result.diagnostics); Assert.Equal(1, result.updates.Length); + AssertEx.Equal(new[] { 0x02000002 }, result.updates[0].UpdatedTypes); solution = solution.WithDocumentText(documentIdA, SourceText.From(source3, Encoding.UTF8)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index b58a6cb1d55ad..2511afe8fbf26 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -740,7 +740,6 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution { Contract.ThrowIfNull(emitResult.Baseline); - // TODO: Pass these to ManagedModuleUpdate in the new debugger contracts API var updatedMethodTokens = emitResult.UpdatedMethods.SelectAsArray(h => MetadataTokens.GetToken(h)); var updatedTypeTokens = emitResult.UpdatedTypes.SelectAsArray(h => MetadataTokens.GetToken(h)); @@ -762,7 +761,7 @@ public async ValueTask EmitSolutionUpdateAsync(Solution solution pdbStream.ToImmutableArray(), projectChanges.LineChanges, updatedMethodTokens, - updatedTypes: ImmutableArray.Empty, + updatedTypeTokens, activeStatementsInUpdatedMethods, exceptionRegionUpdates)); diff --git a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs index c8148eb835f3e..1badd4a005f6f 100644 --- a/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs +++ b/src/Features/Core/Portable/ExternalAccess/Watch/Api/WatchHotReloadService.cs @@ -41,13 +41,15 @@ public readonly struct Update public readonly ImmutableArray ILDelta; public readonly ImmutableArray MetadataDelta; public readonly ImmutableArray PdbDelta; + public readonly ImmutableArray UpdatedTypes; - public Update(Guid moduleId, ImmutableArray ilDelta, ImmutableArray metadataDelta, ImmutableArray pdbDelta) + public Update(Guid moduleId, ImmutableArray ilDelta, ImmutableArray metadataDelta, ImmutableArray pdbDelta, ImmutableArray updatedTypes) { ModuleId = moduleId; ILDelta = ilDelta; MetadataDelta = metadataDelta; PdbDelta = pdbDelta; + UpdatedTypes = updatedTypes; } } @@ -94,7 +96,7 @@ public async Task StartSessionAsync(Solution solution, CancellationToken cancell } var updates = results.ModuleUpdates.Updates.SelectAsArray( - update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta)); + update => new Update(update.Module, update.ILDelta, update.MetadataDelta, update.PdbDelta, update.UpdatedTypes)); var diagnostics = await results.GetAllDiagnosticsAsync(solution, cancellationToken).ConfigureAwait(false); From 47927242033fd58b0cff0825eff3850d942b28c0 Mon Sep 17 00:00:00 2001 From: Chris Sienkiewicz Date: Wed, 23 Jun 2021 19:07:34 -0700 Subject: [PATCH 19/19] Move a couple of APIs from the generator driver into extension methods (#54285) --- .../SourceGeneration/CSharpGeneratorDriver.cs | 2 +- .../Analyzers/AnalyzerFileReferenceTests.cs | 12 +++--- .../Core/Portable/PublicAPI.Unshipped.txt | 5 ++- .../SourceGeneration/GeneratorDriver.cs | 34 ++-------------- .../SourceGeneration/GeneratorExtensions.cs | 39 +++++++++++++++++++ .../SourceGeneratedDocumentIdentity.cs | 2 +- .../TestGeneratorReference.cs | 2 +- 7 files changed, 55 insertions(+), 41 deletions(-) create mode 100644 src/Compilers/Core/Portable/SourceGeneration/GeneratorExtensions.cs diff --git a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs index 5ddbb2bd85b3a..e9858fb922c4e 100644 --- a/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs +++ b/src/Compilers/CSharp/Portable/SourceGeneration/CSharpGeneratorDriver.cs @@ -46,7 +46,7 @@ public static CSharpGeneratorDriver Create(params ISourceGenerator[] generators) /// The incremental generators to create this driver with /// A new instance. public static CSharpGeneratorDriver Create(params IIncrementalGenerator[] incrementalGenerators) - => Create(incrementalGenerators.Select(WrapGenerator), additionalTexts: null); + => Create(incrementalGenerators.Select(GeneratorExtensions.AsSourceGenerator), additionalTexts: null); /// /// Creates a new instance of with the specified s and the provided options or default. diff --git a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs index 5c41caf45b250..6e25a7ef485ad 100644 --- a/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs +++ b/src/Compilers/Core/CodeAnalysisTest/Analyzers/AnalyzerFileReferenceTests.cs @@ -253,7 +253,7 @@ public void TestLoadGenerators() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGeneratorsForAllLanguages(); - var typeNames = generators.Select(g => GeneratorDriver.GetGeneratorType(g).FullName); + var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { @@ -291,7 +291,7 @@ public void TestLoadCSharpGenerators() AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGenerators(LanguageNames.CSharp); - var typeNames = generators.Select(g => GeneratorDriver.GetGeneratorType(g).FullName); + var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { "Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", @@ -313,7 +313,7 @@ public void TestLoadVisualBasicGenerators() AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); var generators = reference.GetGenerators(LanguageNames.VisualBasic); - var typeNames = generators.Select(g => GeneratorDriver.GetGeneratorType(g).FullName); + var typeNames = generators.Select(g => g.GetGeneratorType().FullName); AssertEx.SetEqual(new[] { "Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", @@ -436,7 +436,7 @@ public void TestLoadedGeneratorOrderIsDeterministic() { AnalyzerFileReference reference = CreateAnalyzerFileReference(Assembly.GetExecutingAssembly().Location); - var csharpGenerators = reference.GetGenerators(LanguageNames.CSharp).Select(g => GeneratorDriver.GetGeneratorType(g).FullName).ToArray(); + var csharpGenerators = reference.GetGenerators(LanguageNames.CSharp).Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(10, csharpGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", csharpGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", csharpGenerators[1]); @@ -449,7 +449,7 @@ public void TestLoadedGeneratorOrderIsDeterministic() Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestSourceAndIncrementalGenerator", csharpGenerators[8]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicAndCSharpGenerator", csharpGenerators[9]); - var vbGenerators = reference.GetGenerators(LanguageNames.VisualBasic).Select(g => GeneratorDriver.GetGeneratorType(g).FullName).ToArray(); + var vbGenerators = reference.GetGenerators(LanguageNames.VisualBasic).Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(5, vbGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.CSharpAndVisualBasicGenerator", vbGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.TestIncrementalGenerator", vbGenerators[1]); @@ -458,7 +458,7 @@ public void TestLoadedGeneratorOrderIsDeterministic() Assert.Equal("Microsoft.CodeAnalysis.UnitTests.VisualBasicOnlyGenerator", vbGenerators[4]); // generators load in language order (C#, F#, VB), and *do not* include duplicates - var allGenerators = reference.GetGeneratorsForAllLanguages().Select(g => GeneratorDriver.GetGeneratorType(g).FullName).ToArray(); + var allGenerators = reference.GetGeneratorsForAllLanguages().Select(g => g.GetGeneratorType().FullName).ToArray(); Assert.Equal(12, allGenerators.Length); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+SomeType+NestedGenerator", allGenerators[0]); Assert.Equal("Microsoft.CodeAnalysis.UnitTests.AnalyzerFileReferenceTests+TestGenerator", allGenerators[1]); diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 248e4ebb4f600..18d1e21ab84a1 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -6,6 +6,7 @@ Microsoft.CodeAnalysis.Emit.EmitDifferenceResult.UpdatedMethods.get -> System.Co Microsoft.CodeAnalysis.Emit.EmitDifferenceResult.UpdatedTypes.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.GeneratorAttribute.GeneratorAttribute(string! firstLanguage, params string![]! additionalLanguages) -> void Microsoft.CodeAnalysis.GeneratorAttribute.Languages.get -> string![]! +Microsoft.CodeAnalysis.GeneratorExtensions Microsoft.CodeAnalysis.IFieldSymbol.IsExplicitlyNamedTupleElement.get -> bool Microsoft.CodeAnalysis.GeneratorExecutionContext.SyntaxContextReceiver.get -> Microsoft.CodeAnalysis.ISyntaxContextReceiver? Microsoft.CodeAnalysis.GeneratorInitializationContext.RegisterForSyntaxNotifications(Microsoft.CodeAnalysis.SyntaxContextReceiverCreator! receiverCreator) -> void @@ -86,10 +87,10 @@ override Microsoft.CodeAnalysis.Operations.OperationWalker.DefaultVis override Microsoft.CodeAnalysis.Operations.OperationWalker.Visit(Microsoft.CodeAnalysis.IOperation? operation, TArgument argument) -> object? static Microsoft.CodeAnalysis.FileLinePositionSpan.operator !=(Microsoft.CodeAnalysis.FileLinePositionSpan left, Microsoft.CodeAnalysis.FileLinePositionSpan right) -> bool static Microsoft.CodeAnalysis.FileLinePositionSpan.operator ==(Microsoft.CodeAnalysis.FileLinePositionSpan left, Microsoft.CodeAnalysis.FileLinePositionSpan right) -> bool +static Microsoft.CodeAnalysis.GeneratorExtensions.AsSourceGenerator(this Microsoft.CodeAnalysis.IIncrementalGenerator! incrementalGenerator) -> Microsoft.CodeAnalysis.ISourceGenerator! +static Microsoft.CodeAnalysis.GeneratorExtensions.GetGeneratorType(this Microsoft.CodeAnalysis.ISourceGenerator! generator) -> System.Type! static Microsoft.CodeAnalysis.LineMapping.operator !=(Microsoft.CodeAnalysis.LineMapping left, Microsoft.CodeAnalysis.LineMapping right) -> bool static Microsoft.CodeAnalysis.LineMapping.operator ==(Microsoft.CodeAnalysis.LineMapping left, Microsoft.CodeAnalysis.LineMapping right) -> bool -static Microsoft.CodeAnalysis.GeneratorDriver.GetGeneratorType(Microsoft.CodeAnalysis.ISourceGenerator! generator) -> System.Type! -static Microsoft.CodeAnalysis.GeneratorDriver.WrapGenerator(Microsoft.CodeAnalysis.IIncrementalGenerator! incrementalGenerator) -> Microsoft.CodeAnalysis.ISourceGenerator! static Microsoft.CodeAnalysis.IncrementalValueProviderExtensions.Collect(this Microsoft.CodeAnalysis.IncrementalValuesProvider source) -> Microsoft.CodeAnalysis.IncrementalValueProvider> static Microsoft.CodeAnalysis.IncrementalValueProviderExtensions.Combine(this Microsoft.CodeAnalysis.IncrementalValueProvider provider1, Microsoft.CodeAnalysis.IncrementalValueProvider provider2) -> Microsoft.CodeAnalysis.IncrementalValueProvider<(TLeft Left, TRight Right)> static Microsoft.CodeAnalysis.IncrementalValueProviderExtensions.Combine(this Microsoft.CodeAnalysis.IncrementalValuesProvider provider1, Microsoft.CodeAnalysis.IncrementalValueProvider provider2) -> Microsoft.CodeAnalysis.IncrementalValuesProvider<(TLeft Left, TRight Right)> diff --git a/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs b/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs index 357540bdbf78b..13abd36b98473 100644 --- a/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs +++ b/src/Compilers/Core/Portable/SourceGeneration/GeneratorDriver.cs @@ -29,7 +29,7 @@ public abstract class GeneratorDriver internal GeneratorDriver(GeneratorDriverState state) { - Debug.Assert(state.Generators.GroupBy(s => GetGeneratorType(s)).Count() == state.Generators.Length); // ensure we don't have duplicate generator types + Debug.Assert(state.Generators.GroupBy(s => s.GetGeneratorType()).Count() == state.Generators.Length); // ensure we don't have duplicate generator types _state = state; } @@ -130,32 +130,6 @@ static ImmutableArray getGeneratorSources(GeneratorState } } - /// - /// Returns the underlying type of a given generator - /// - /// - /// For s we create a wrapper type that also implements - /// . This method will unwrap and return the underlying type - /// in those cases. - /// - /// The generator to get the type of - /// The underlying generator type - public static Type GetGeneratorType(ISourceGenerator generator) - { - if (generator is IncrementalGeneratorWrapper igw) - { - return igw.Generator.GetType(); - } - return generator.GetType(); - } - - /// - /// Wraps an in an object that can be used to construct a - /// - /// The incremental generator to wrap - /// A wrapped generator that can be passed to a generator driver - public static ISourceGenerator WrapGenerator(IIncrementalGenerator incrementalGenerator) => new IncrementalGeneratorWrapper(incrementalGenerator); - internal GeneratorDriverState RunGeneratorsCore(Compilation compilation, DiagnosticBag? diagnosticsBag, CancellationToken cancellationToken = default) { // with no generators, there is no work to do @@ -281,7 +255,7 @@ private IncrementalExecutionContext UpdateOutputs(ImmutableArray ParseAdditionalSources(ISourceGenerator generator, ImmutableArray generatedSources, CancellationToken cancellationToken) { var trees = ArrayBuilder.GetInstance(generatedSources.Length); - var type = GetGeneratorType(generator); + var type = generator.GetGeneratorType(); var prefix = GetFilePathPrefixForGenerator(generator); foreach (var source in generatedSources) { @@ -311,7 +285,7 @@ private static GeneratorState SetGeneratorException(CommonMessageProvider provid isEnabledByDefault: true, customTags: WellKnownDiagnosticTags.AnalyzerException); - var diagnostic = Diagnostic.Create(descriptor, Location.None, GetGeneratorType(generator).Name, e.GetType().Name, e.Message); + var diagnostic = Diagnostic.Create(descriptor, Location.None, generator.GetGeneratorType().Name, e.GetType().Name, e.Message); diagnosticBag?.Add(diagnostic); return new GeneratorState(generatorState.Info, e, diagnostic); @@ -334,7 +308,7 @@ private static ImmutableArray FilterDiagnostics(Compilation compilat internal static string GetFilePathPrefixForGenerator(ISourceGenerator generator) { - var type = GetGeneratorType(generator); + var type = generator.GetGeneratorType(); return Path.Combine(type.Assembly.GetName().Name ?? string.Empty, type.FullName!); } diff --git a/src/Compilers/Core/Portable/SourceGeneration/GeneratorExtensions.cs b/src/Compilers/Core/Portable/SourceGeneration/GeneratorExtensions.cs new file mode 100644 index 0000000000000..dd6dea79380f0 --- /dev/null +++ b/src/Compilers/Core/Portable/SourceGeneration/GeneratorExtensions.cs @@ -0,0 +1,39 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Microsoft.CodeAnalysis +{ + public static class GeneratorExtensions + { + /// + /// Returns the underlying type of a given generator + /// + /// + /// For s a wrapper is created that also implements + /// . This method will unwrap and return the underlying type + /// in those cases. + /// + /// The generator to get the type of + /// The underlying generator type + public static Type GetGeneratorType(this ISourceGenerator generator) + { + if (generator is IncrementalGeneratorWrapper igw) + { + return igw.Generator.GetType(); + } + return generator.GetType(); + } + + /// + /// Converts an in an object that can be used when constructing a + /// + /// The incremental generator to wrap + /// A wrapped generator that can be passed to a generator driver + public static ISourceGenerator AsSourceGenerator(this IIncrementalGenerator incrementalGenerator) => new IncrementalGeneratorWrapper(incrementalGenerator); + } +} diff --git a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs index c2d03ef939c66..72ea5f15aa772 100644 --- a/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs +++ b/src/Workspaces/Core/Portable/Workspace/Solution/SourceGeneratedDocumentIdentity.cs @@ -36,7 +36,7 @@ public SourceGeneratedDocumentIdentity(DocumentId documentId, string hintName, s public static string GetGeneratorTypeName(ISourceGenerator generator) { - return GeneratorDriver.GetGeneratorType(generator).FullName!; + return generator.GetGeneratorType().FullName!; } public static string GetGeneratorAssemblyName(ISourceGenerator generator) diff --git a/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs b/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs index 6650c252fe590..fff413c2ac07c 100644 --- a/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs +++ b/src/Workspaces/CoreTestUtilities/TestGeneratorReference.cs @@ -34,7 +34,7 @@ public TestGeneratorReference(ISourceGenerator generator) } public TestGeneratorReference(IIncrementalGenerator generator) - : this(GeneratorDriver.WrapGenerator(generator)) + : this(generator.AsSourceGenerator()) { }