Skip to content
Permalink
Browse files

Merge pull request #39745 from allisonchou/ExtractLocalFunction

Add code action to extract local functions
  • Loading branch information
allisonchou committed Nov 28, 2019
2 parents 8d19959 + d82e77e commit 64b63ee02b8c8a21e048b517a98ad1a10e51fac4
Showing with 5,123 additions and 564 deletions.
  1. +4,051 −0 src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractLocalFunctionTests.cs
  2. +140 −27 src/EditorFeatures/CSharpTest/CodeActions/ExtractMethod/ExtractMethodTests.cs
  3. +1 −1 src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodBase.cs
  4. +2 −2 src/EditorFeatures/CSharpTest/ExtractMethod/ExtractMethodTests.cs
  5. +2 −2 src/EditorFeatures/Core/Implementation/ExtractMethod/AbstractExtractMethodCommandHandler.cs
  6. +0 −9 src/Features/CSharp/Portable/CSharpFeaturesResources.Designer.cs
  7. +0 −3 src/Features/CSharp/Portable/CSharpFeaturesResources.resx
  8. +2 −2 src/Features/CSharp/Portable/ExtractMethod/CSharpExtractMethodService.cs
  9. +4 −4 src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.Analyzer.cs
  10. +4 −3 ...Sharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.ExpressionCodeGenerator.cs
  11. +4 −10 ...rtable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.MultipleStatementsCodeGenerator.cs
  12. +4 −10 .../Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.SingleStatementCodeGenerator.cs
  13. +107 −51 src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.CSharpCodeGenerator.cs
  14. +34 −17 src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.TriviaResult.cs
  15. +50 −6 src/Features/CSharp/Portable/ExtractMethod/CSharpMethodExtractor.cs
  16. +3 −1 src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionResult.StatementResult.cs
  17. +0 −48 src/Features/CSharp/Portable/ExtractMethod/CSharpSelectionValidator.cs
  18. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.cs.xlf
  19. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.de.xlf
  20. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.es.xlf
  21. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.fr.xlf
  22. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.it.xlf
  23. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ja.xlf
  24. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ko.xlf
  25. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pl.xlf
  26. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.pt-BR.xlf
  27. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.ru.xlf
  28. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.tr.xlf
  29. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hans.xlf
  30. +0 −5 src/Features/CSharp/Portable/xlf/CSharpFeaturesResources.zh-Hant.xlf
  31. +48 −11 ...ures/Core/Portable/CodeRefactorings/ExtractMethod/AbstractExtractMethodCodeRefactoringProvider.cs
  32. +1 −1 src/Features/Core/Portable/ExtractInterface/ExtractInterfaceCodeAction.cs
  33. +3 −2 src/Features/Core/Portable/ExtractMethod/AbstractExtractMethodService.cs
  34. +3 −2 src/Features/Core/Portable/ExtractMethod/ExtractMethodService.cs
  35. +1 −1 src/Features/Core/Portable/ExtractMethod/IExtractMethodService.cs
  36. +21 −14 src/Features/Core/Portable/ExtractMethod/MethodExtractor.Analyzer.cs
  37. +28 −13 src/Features/Core/Portable/ExtractMethod/MethodExtractor.CodeGenerator.cs
  38. +29 −12 src/Features/Core/Portable/ExtractMethod/MethodExtractor.cs
  39. +1 −0 src/Features/Core/Portable/ExtractMethod/OperationStatus_Statics.cs
  40. +2 −1 src/Features/Core/Portable/ExtractMethod/UniqueNameGenerator.cs
  41. +27 −9 src/Features/Core/Portable/FeaturesResources.Designer.cs
  42. +12 −6 src/Features/Core/Portable/FeaturesResources.resx
  43. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf
  44. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.de.xlf
  45. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.es.xlf
  46. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf
  47. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.it.xlf
  48. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf
  49. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf
  50. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf
  51. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf
  52. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf
  53. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf
  54. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf
  55. +25 −15 src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf
  56. +1 −1 src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicExtractMethodService.vb
  57. +1 −1 src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.Analyzer.vb
  58. +1 −1 ...able/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.ExpressionCodeGenerator.vb
  59. +1 −1 ...ractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.MultipleStatementsCodeGenerator.vb
  60. +1 −1 ...ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.SingleStatementCodeGenerator.vb
  61. +2 −2 ...eatures/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.VisualBasicCodeGenerator.vb
  62. +10 −2 src/Features/VisualBasic/Portable/ExtractMethod/VisualBasicMethodExtractor.vb
  63. +1 −0 src/Test/Utilities/Portable/Traits/Traits.cs
  64. +1 −1 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpCodeActions.cs
  65. +8 −8 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpExtractInterfaceDialog.cs
  66. +2 −2 src/VisualStudio/IntegrationTest/IntegrationTests/CSharp/CSharpExtractMethod.cs
  67. +5 −5 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicExtractInterfaceDialog.cs
  68. +2 −2 src/VisualStudio/IntegrationTest/IntegrationTests/VisualBasic/BasicExtractMethod.cs
  69. +13 −0 src/Workspaces/CSharp/Portable/CodeGeneration/CSharpCodeGenerationService.cs
  70. +75 −6 src/Workspaces/CSharp/Portable/CodeGeneration/MethodGenerator.cs
  71. +3 −0 src/Workspaces/CSharp/Portable/LanguageServices/CSharpSyntaxFactsService.cs
  72. +2 −2 src/Workspaces/Core/Portable/CodeGeneration/AbstractCodeGenerationService.cs
  73. +2 −1 src/Workspaces/Core/Portable/CodeGeneration/Symbols/CodeGenerationMethodSymbol.cs
  74. +2 −0 src/Workspaces/Core/Portable/LanguageServices/SyntaxFactsService/ISyntaxFactsService.cs
  75. +9 −0 src/Workspaces/Core/Portable/WorkspacesResources.Designer.cs
  76. +3 −0 src/Workspaces/Core/Portable/WorkspacesResources.resx
  77. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.cs.xlf
  78. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.de.xlf
  79. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.es.xlf
  80. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.fr.xlf
  81. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.it.xlf
  82. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.ja.xlf
  83. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.ko.xlf
  84. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.pl.xlf
  85. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.pt-BR.xlf
  86. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.ru.xlf
  87. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.tr.xlf
  88. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hans.xlf
  89. +5 −0 src/Workspaces/Core/Portable/xlf/WorkspacesResources.zh-Hant.xlf
  90. +4 −0 src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb

Large diffs are not rendered by default.

@@ -1478,26 +1478,41 @@ private static string NewMethod(int i)
[WorkItem(15532, "https://github.com/dotnet/roslyn/issues/15532")]
public async Task ExtractLocalFunctionCall()
{
await TestInRegularAndScriptAsync(@"
var code = @"
class C
{
public static void Main()
{
void Local() { }
[|Local();|]
}
}";
await TestExactActionSetOfferedAsync(code, new[] { FeaturesResources.Extract_local_function });
}

[Fact]
public async Task ExtractLocalFunctionCall_2()
{
await TestInRegularAndScriptAsync(@"
class C
{
public static void Main()
{
[|void Local() { }
Local();|]
}
}", @"
class C
{
public static void Main()
{
void Local() { }
{|Rename:NewMethod|}();
}
private static void NewMethod()
{
{|Warning:Local();|}
void Local() { }
Local();
}
}");
}
@@ -1506,28 +1521,16 @@ private static void NewMethod()
[WorkItem(15532, "https://github.com/dotnet/roslyn/issues/15532")]
public async Task ExtractLocalFunctionCallWithCapture()
{
await TestInRegularAndScriptAsync(@"
var code = @"
class C
{
public static void Main(string[] args)
{
bool Local() => args == null;
[|Local();|]
}
}", @"
class C
{
public static void Main(string[] args)
{
bool Local() => args == null;
{|Rename:NewMethod|}(args);
}
private static void NewMethod(string[] args)
{
{|Warning:Local();|}
}
}");
}";
await TestExactActionSetOfferedAsync(code, new[] { FeaturesResources.Extract_local_function });
}

[Fact]
@@ -1567,9 +1570,9 @@ class C
public static void Main()
{
void Local()
{|Warning:{
{
{|Rename:NewMethod|}();
}|}
}
Local();
}
@@ -1616,7 +1619,7 @@ static void Main(string[] args)
private static int NewMethod(int v, int i)
{
{|Warning:v = v + i;|}
v = v + i;
return v;
}
}");
@@ -1650,7 +1653,7 @@ static void Main(string[] args)
int v = 0;
for(int i=0 ; i<5; i++)
{
{|Warning:v = {|Rename:NewMethod|}(v, i)|};
v = {|Rename:NewMethod|}(v, i);
}
}
}
@@ -1690,7 +1693,7 @@ static void Main(string[] args)
int v = 0;
for(int i=0 ; i<5; i++)
{
{|Warning:i = {|Rename:NewMethod|}(ref v, i)|};
i = {|Rename:NewMethod|}(ref v, i);
}
}
}
@@ -3125,6 +3128,116 @@ void M()
}");

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task EnsureStaticLocalFunctionOptionHasNoEffect()
{
await TestInRegularAndScriptAsync(
@"class Program
{
static void Main(string[] args)
{
bool b = true;
System.Console.WriteLine([|b != true|] ? b = true : b = false);
}
}",
@"class Program
{
static void Main(string[] args)
{
bool b = true;
System.Console.WriteLine({|Rename:NewMethod|}(b) ? b = true : b = false);
}
private static bool NewMethod(bool b)
{
return b != true;
}
}", options: Option(CSharpCodeStyleOptions.PreferStaticLocalFunction, CodeStyleOptions.FalseWithSuggestionEnforcement));
}

[Fact(Skip = "https://github.com/dotnet/roslyn/issues/39946"), Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task ExtractLocalFunctionCallAndDeclaration()
{
await TestInRegularAndScriptAsync(@"
class C
{
public static void Main()
{
static void LocalParent()
{
[|void Local() { }
Local();|]
}
}
}", @"
class C
{
public static void Main()
{
static void LocalParent()
{
{|Rename:NewMethod|}();
}
}
private static void NewMethod()
{
void Local() { }
Local();
}
}");
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestMissingWhenOnlyLocalFunctionCallSelected()
{
var code = @"
class Program
{
static void Main(string[] args)
{
[|Local();|]
static void Local()
{
}
}
}";
await TestExactActionSetOfferedAsync(code, new[] { FeaturesResources.Extract_local_function });
}

[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestOfferedWhenBothLocalFunctionCallAndDeclarationSelected()
{
await TestInRegularAndScriptAsync(@"
class Program
{
static void Main(string[] args)
{
[|Local();
var test = 5;
static void Local()
{
}|]
}
}", @"
class Program
{
static void Main(string[] args)
{
{|Rename:NewMethod|}();
}
private static void NewMethod()
{
Local();
var test = 5;
static void Local()
{
}
}
}");
}

[Fact, WorkItem(38529, "https://github.com/dotnet/roslyn/issues/38529"), Trait(Traits.Feature, Traits.Features.CodeActionsExtractMethod)]
public async Task TestExtractNonAsyncMethodWithAsyncLocalFunction()
{
await TestInRegularAndScriptAsync(
@@ -3139,9 +3252,9 @@ void M()
@"class C
{
void M()
{|Warning:{
{
{|Rename:NewMethod|}();
}|}
}
private static void NewMethod()
{
@@ -3347,9 +3460,9 @@ async Task MyDelay(TimeSpan duration)
class C
{
async Task MyDelay(TimeSpan duration)
{|Warning:{
{
await {|Rename:NewMethod|}(duration);
}|}
}
private static async Task NewMethod(TimeSpan duration)
{
@@ -137,7 +137,7 @@ protected async Task NotSupported_ExtractMethodAsync(string codeWithMarker)
Assert.True(selectedCode.ContainsValidContext);

// extract method
var extractor = new CSharpMethodExtractor((CSharpSelectionResult)selectedCode);
var extractor = new CSharpMethodExtractor((CSharpSelectionResult)selectedCode, localFunction: false);
var result = await extractor.ExtractMethodAsync(CancellationToken.None);
Assert.NotNull(result);
Assert.Equal(succeed,
@@ -10329,7 +10329,7 @@ public async Task ExtractMethod_Argument1()
var service = new CSharpExtractMethodService();
Assert.NotNull(await Record.ExceptionAsync(async () =>
{
var tree = await service.ExtractMethodAsync(null, default, null, CancellationToken.None);
var tree = await service.ExtractMethodAsync(document: null, textSpan: default, localFunction: false, options: null, CancellationToken.None);
}));
}

@@ -10345,7 +10345,7 @@ public async Task ExtractMethod_Argument2()

var service = new CSharpExtractMethodService() as IExtractMethodService;

await service.ExtractMethodAsync(document, default);
await service.ExtractMethodAsync(document, textSpan: default, localFunction: false);
}

[WpfFact]
@@ -99,7 +99,7 @@ public bool ExecuteCommand(ExtractMethodCommandArgs args, CommandExecutionContex
}

var result = ExtractMethodService.ExtractMethodAsync(
document, spans.Single().Span.ToTextSpan(), cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken);
document, spans.Single().Span.ToTextSpan(), localFunction: false, cancellationToken: cancellationToken).WaitAndGetResult(cancellationToken);
Contract.ThrowIfNull(result);

if (!result.Succeeded && !result.SucceededWithSuggestion)
@@ -220,7 +220,7 @@ private bool TryNotifyFailureToUser(Document document, ExtractMethodResult resul
{
options = options.WithChangedOption(ExtractMethodOptions.DontPutOutOrRefOnStruct, document.Project.Language, true);
var newResult = ExtractMethodService.ExtractMethodAsync(
document, spans.Single().Span.ToTextSpan(), options, cancellationToken).WaitAndGetResult(cancellationToken);
document, spans.Single().Span.ToTextSpan(), localFunction: false, options, cancellationToken).WaitAndGetResult(cancellationToken);

// retry succeeded, return new result
if (newResult.Succeeded || newResult.SucceededWithSuggestion)

Some generated files are not rendered by default. Learn more.

@@ -523,9 +523,6 @@
<data name="Convert_to_switch_expression" xml:space="preserve">
<value>Convert to 'switch' expression</value>
</data>
<data name="Warning_Extracting_a_local_function_reference_may_produce_invalid_code" xml:space="preserve">
<value>Warning: Extracting a local function reference may produce invalid code</value>
</data>
<data name="Name" xml:space="preserve">
<value>&lt;Name&gt;</value>
</data>
@@ -22,9 +22,9 @@ protected override CSharpSelectionValidator CreateSelectionValidator(SemanticDoc
return new CSharpSelectionValidator(document, textSpan, options);
}

protected override CSharpMethodExtractor CreateMethodExtractor(CSharpSelectionResult selectionResult)
protected override CSharpMethodExtractor CreateMethodExtractor(CSharpSelectionResult selectionResult, bool localFunction)
{
return new CSharpMethodExtractor(selectionResult);
return new CSharpMethodExtractor(selectionResult, localFunction);
}
}
}
@@ -20,14 +20,14 @@ private class CSharpAnalyzer : Analyzer
{
private static readonly HashSet<int> s_nonNoisySyntaxKindSet = new HashSet<int>(new int[] { (int)SyntaxKind.WhitespaceTrivia, (int)SyntaxKind.EndOfLineTrivia });

public static Task<AnalyzerResult> AnalyzeAsync(SelectionResult selectionResult, CancellationToken cancellationToken)
public static Task<AnalyzerResult> AnalyzeAsync(SelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken)
{
var analyzer = new CSharpAnalyzer(selectionResult, cancellationToken);
var analyzer = new CSharpAnalyzer(selectionResult, localFunction, cancellationToken);
return analyzer.AnalyzeAsync();
}

public CSharpAnalyzer(SelectionResult selectionResult, CancellationToken cancellationToken)
: base(selectionResult, cancellationToken)
public CSharpAnalyzer(SelectionResult selectionResult, bool localFunction, CancellationToken cancellationToken)
: base(selectionResult, localFunction, cancellationToken)
{
}

@@ -21,8 +21,9 @@ private class ExpressionCodeGenerator : CSharpCodeGenerator
public ExpressionCodeGenerator(
InsertionPoint insertionPoint,
SelectionResult selectionResult,
AnalyzerResult analyzerResult)
: base(insertionPoint, selectionResult, analyzerResult)
AnalyzerResult analyzerResult,
bool localFunction)
: base(insertionPoint, selectionResult, analyzerResult, localFunction)
{
}

@@ -31,7 +32,7 @@ public static bool IsExtractMethodOnExpression(SelectionResult code)
return code.SelectionInExpression;
}

protected override SyntaxToken CreateMethodName()
protected override SyntaxToken CreateMethodName(bool localFunction)
{
var methodName = "NewMethod";
var containingScope = this.CSharpSelectionResult.GetContainingScope();

0 comments on commit 64b63ee

Please sign in to comment.
You can’t perform that action at this time.