diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs index 9b3e1eaeb338f471a8fb37c92fa388e08c1d9806..2c11e692dca611f5f50f73abb23e1cacbf051a8d 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.Members.cs @@ -564,7 +564,7 @@ private void AddMemberModifiersIfRequired(ISymbol symbol) if (format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeModifiers) && (containingType == null || - (containingType.TypeKind != TypeKind.Interface && !IsEnumMember(symbol)))) + (containingType.TypeKind != TypeKind.Interface && !IsEnumMember(symbol) && !IsLocalFunction(symbol)))) { var isConst = symbol is IFieldSymbol && ((IFieldSymbol)symbol).IsConst; if (symbol.IsStatic && !isConst) diff --git a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs index 33940b965339a39c1140a9ad0539d8fd970a23ee..7708e248096377ad9438db0aacc16c73c86b143a 100644 --- a/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs +++ b/src/Compilers/CSharp/Portable/SymbolDisplay/SymbolDisplayVisitor.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.PooledObjects; @@ -287,12 +288,22 @@ private void AddAccessibilityIfRequired(ISymbol symbol) if (format.MemberOptions.IncludesOption(SymbolDisplayMemberOptions.IncludeAccessibility) && (containingType == null || - (containingType.TypeKind != TypeKind.Interface && !IsEnumMember(symbol)))) + (containingType.TypeKind != TypeKind.Interface && !IsEnumMember(symbol) & !IsLocalFunction(symbol)))) { AddAccessibility(symbol); } } + private static bool IsLocalFunction(ISymbol symbol) + { + if (symbol.Kind != SymbolKind.Method) + { + return false; + } + + return ((IMethodSymbol)symbol).MethodKind == MethodKind.LocalFunction; + } + private void AddAccessibility(ISymbol symbol) { switch (symbol.DeclaredAccessibility) diff --git a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs index 3d8c257ced9768c9d29fbd4f5bf773b9e48f68a7..0dde89c8471c492ada9fea94d549bfb779f6b164 100644 --- a/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/SymbolDisplay/SymbolDisplayTests.cs @@ -5854,6 +5854,47 @@ void M() SymbolDisplayPartKind.Punctuation); // ) } + [Fact] + [CompilerTrait(CompilerFeature.LocalFunctions)] + public void LocalFunctionForChangeSignature() + { + SymbolDisplayFormat changeSignatureFormat = new SymbolDisplayFormat( + genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, + miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes, + extensionMethodStyle: SymbolDisplayExtensionMethodStyle.StaticMethod, + memberOptions: + SymbolDisplayMemberOptions.IncludeType | + SymbolDisplayMemberOptions.IncludeExplicitInterface | + SymbolDisplayMemberOptions.IncludeAccessibility | + SymbolDisplayMemberOptions.IncludeModifiers | + SymbolDisplayMemberOptions.IncludeRef); + + var srcTree = SyntaxFactory.ParseSyntaxTree(@" +class C +{ + void M() + { + void Local() {} + } +}"); + var root = srcTree.GetRoot(); + var comp = CreateCompilation(srcTree); + + var semanticModel = comp.GetSemanticModel(srcTree); + var local = root.DescendantNodes() + .Where(n => n.Kind() == SyntaxKind.LocalFunctionStatement) + .Single(); + var localSymbol = Assert.IsType( + semanticModel.GetDeclaredSymbol(local)); + + Verify(localSymbol.ToDisplayParts(changeSignatureFormat), + "void Local", + SymbolDisplayPartKind.Keyword, // void + SymbolDisplayPartKind.Space, + SymbolDisplayPartKind.MethodName // Local + ); + } + [Fact] [CompilerTrait(CompilerFeature.LocalFunctions)] public void LocalFunction2() diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs index ab73acf3708ad75c4ac85291f8dd3cd5abd7f9a3..73bb9f3a64305b40f85080c98d53c2d8bf2eebfc 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/ReorderParametersTests.cs @@ -9,6 +9,70 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ChangeSignature { public partial class ChangeSignatureTests : AbstractChangeSignatureTests { + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task ReorderLocalFunctionParametersAndArguments_OnDeclaration() + { + var markup = @" +using System; +class MyClass +{ + public void M() + { + Goo(1, 2); + void $$Goo(int x, string y) + { + } + } +}"; + var permutation = new[] { 1, 0 }; + var updatedCode = @" +using System; +class MyClass +{ + public void M() + { + Goo(2, 1); + void Goo(string y, int x) + { + } + } +}"; + + await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); + } + + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task ReorderLocalFunctionParametersAndArguments_OnInvocation() + { + var markup = @" +using System; +class MyClass +{ + public void M() + { + $$Goo(1, null); + void Goo(int x, string y) + { + } + } +}"; + var permutation = new[] { 1, 0 }; + var updatedCode = @" +using System; +class MyClass +{ + public void M() + { + Goo(null, 1); + void Goo(string y, int x) + { + } + } +}"; + + await TestChangeSignatureViaCommandAsync(LanguageNames.CSharp, markup, updatedSignature: permutation, expectedUpdatedInvocationDocumentCode: updatedCode); + } + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] public async Task ReorderMethodParameters() { diff --git a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs index 5a84de6a9ef05153a9b5f2d9318149ee20aa2c3b..ba20917b8a391369d93814a4bddad23e5a980efb 100644 --- a/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs +++ b/src/Features/CSharp/Portable/ChangeSignature/CSharpChangeSignatureService.cs @@ -28,7 +28,8 @@ internal sealed class CSharpChangeSignatureService : AbstractChangeSignatureServ SyntaxKind.IndexerDeclaration, SyntaxKind.DelegateDeclaration, SyntaxKind.SimpleLambdaExpression, - SyntaxKind.ParenthesizedLambdaExpression); + SyntaxKind.ParenthesizedLambdaExpression, + SyntaxKind.LocalFunctionStatement); private static readonly ImmutableArray _declarationAndInvocableKinds = _declarationKinds.Concat(ImmutableArray.Create( @@ -56,6 +57,7 @@ internal sealed class CSharpChangeSignatureService : AbstractChangeSignatureServ private static readonly ImmutableArray _updatableNodeKinds = ImmutableArray.Create( SyntaxKind.MethodDeclaration, + SyntaxKind.LocalFunctionStatement, SyntaxKind.ConstructorDeclaration, SyntaxKind.IndexerDeclaration, SyntaxKind.InvocationExpression, @@ -253,35 +255,42 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.MethodDeclaration)) { - var method = updatedNode as MethodDeclarationSyntax; + var method = (MethodDeclarationSyntax)updatedNode; var updatedParameters = PermuteDeclaration(method.ParameterList.Parameters, signaturePermutation); return method.WithParameterList(method.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } + if (updatedNode.IsKind(SyntaxKind.LocalFunctionStatement)) + { + var localFunction = (LocalFunctionStatementSyntax)updatedNode; + var updatedParameters = PermuteDeclaration(localFunction.ParameterList.Parameters, signaturePermutation); + return localFunction.WithParameterList(localFunction.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); + } + if (updatedNode.IsKind(SyntaxKind.ConstructorDeclaration)) { - var constructor = updatedNode as ConstructorDeclarationSyntax; + var constructor = (ConstructorDeclarationSyntax)updatedNode; var updatedParameters = PermuteDeclaration(constructor.ParameterList.Parameters, signaturePermutation); return constructor.WithParameterList(constructor.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } if (updatedNode.IsKind(SyntaxKind.IndexerDeclaration)) { - var indexer = updatedNode as IndexerDeclarationSyntax; + var indexer = (IndexerDeclarationSyntax)updatedNode; var updatedParameters = PermuteDeclaration(indexer.ParameterList.Parameters, signaturePermutation); return indexer.WithParameterList(indexer.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } if (updatedNode.IsKind(SyntaxKind.DelegateDeclaration)) { - var delegateDeclaration = updatedNode as DelegateDeclarationSyntax; + var delegateDeclaration = (DelegateDeclarationSyntax)updatedNode; var updatedParameters = PermuteDeclaration(delegateDeclaration.ParameterList.Parameters, signaturePermutation); return delegateDeclaration.WithParameterList(delegateDeclaration.ParameterList.WithParameters(updatedParameters).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } if (updatedNode.IsKind(SyntaxKind.AnonymousMethodExpression)) { - var anonymousMethod = updatedNode as AnonymousMethodExpressionSyntax; + var anonymousMethod = (AnonymousMethodExpressionSyntax)updatedNode; // Delegates may omit parameters in C# if (anonymousMethod.ParameterList == null) @@ -295,7 +304,7 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.SimpleLambdaExpression)) { - var lambda = updatedNode as SimpleLambdaExpressionSyntax; + var lambda = (SimpleLambdaExpressionSyntax)updatedNode; if (signaturePermutation.UpdatedConfiguration.ToListOfParameters().Any()) { @@ -315,7 +324,7 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.ParenthesizedLambdaExpression)) { - var lambda = updatedNode as ParenthesizedLambdaExpressionSyntax; + var lambda = (ParenthesizedLambdaExpressionSyntax)updatedNode; var updatedParameters = PermuteDeclaration(lambda.ParameterList.Parameters, signaturePermutation); return lambda.WithParameterList(lambda.ParameterList.WithParameters(updatedParameters)); } @@ -324,10 +333,10 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.InvocationExpression)) { - var invocation = updatedNode as InvocationExpressionSyntax; + var invocation = (InvocationExpressionSyntax)updatedNode; var semanticModel = document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken); - var symbolInfo = semanticModel.GetSymbolInfo(originalNode as InvocationExpressionSyntax, cancellationToken); + var symbolInfo = semanticModel.GetSymbolInfo((InvocationExpressionSyntax)originalNode, cancellationToken); var methodSymbol = symbolInfo.Symbol as IMethodSymbol; var isReducedExtensionMethod = false; @@ -342,7 +351,7 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.ObjectCreationExpression)) { - var objCreation = updatedNode as ObjectCreationExpressionSyntax; + var objCreation = (ObjectCreationExpressionSyntax)updatedNode; var newArguments = PermuteArgumentList(document, declarationSymbol, objCreation.ArgumentList.Arguments, signaturePermutation); return objCreation.WithArgumentList(objCreation.ArgumentList.WithArguments(newArguments).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } @@ -350,21 +359,21 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.ThisConstructorInitializer) || updatedNode.IsKind(SyntaxKind.BaseConstructorInitializer)) { - var objCreation = updatedNode as ConstructorInitializerSyntax; + var objCreation = (ConstructorInitializerSyntax)updatedNode; var newArguments = PermuteArgumentList(document, declarationSymbol, objCreation.ArgumentList.Arguments, signaturePermutation); return objCreation.WithArgumentList(objCreation.ArgumentList.WithArguments(newArguments).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } if (updatedNode.IsKind(SyntaxKind.ElementAccessExpression)) { - var elementAccess = updatedNode as ElementAccessExpressionSyntax; + var elementAccess = (ElementAccessExpressionSyntax)updatedNode; var newArguments = PermuteArgumentList(document, declarationSymbol, elementAccess.ArgumentList.Arguments, signaturePermutation); return elementAccess.WithArgumentList(elementAccess.ArgumentList.WithArguments(newArguments).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } if (updatedNode.IsKind(SyntaxKind.Attribute)) { - var attribute = updatedNode as AttributeSyntax; + var attribute = (AttributeSyntax)updatedNode; var newArguments = PermuteAttributeArgumentList(document, declarationSymbol, attribute.ArgumentList.Arguments, signaturePermutation); return attribute.WithArgumentList(attribute.ArgumentList.WithArguments(newArguments).WithAdditionalAnnotations(changeSignatureFormattingAnnotation)); } @@ -373,7 +382,7 @@ private SyntaxNode GetNodeContainingTargetNode(SyntaxNode matchingNode) if (updatedNode.IsKind(SyntaxKind.NameMemberCref)) { - var nameMemberCref = updatedNode as NameMemberCrefSyntax; + var nameMemberCref = (NameMemberCrefSyntax)updatedNode; if (nameMemberCref.Parameters == null || !nameMemberCref.Parameters.Parameters.Any())