diff --git a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs index 0a9c490d74d20b82fb713aaa292a59dad2892eea..6fe72c99263f0b590eb82caed7171081ac019fa9 100644 --- a/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs +++ b/src/EditorFeatures/CSharpTest/ChangeSignature/ChangeSignatureTests.cs @@ -186,5 +186,20 @@ void Foo(int a, int b) await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction: false); } + + [WorkItem(17309, "https://github.com/dotnet/roslyn/issues/17309")] + [WpfFact, Trait(Traits.Feature, Traits.Features.ChangeSignature)] + public async Task TestNotInConstraints() + { + var markup = @" +class Ext +{ + void Foo(int a, int b) where [||]T : class + { + }; +}"; + + await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction: false); + } } } \ No newline at end of file diff --git a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj index ae1a13b0a440ad6c2782b818f13da209f384e724..c2d819e876b94b359277470fcdfd0d2680d77109 100644 --- a/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj +++ b/src/EditorFeatures/VisualBasicTest/BasicEditorServicesTest.vbproj @@ -166,6 +166,7 @@ + diff --git a/src/EditorFeatures/VisualBasicTest/ChangeSignature/ChangeSignatureTests.vb b/src/EditorFeatures/VisualBasicTest/ChangeSignature/ChangeSignatureTests.vb new file mode 100644 index 0000000000000000000000000000000000000000..199c5db0beb79fca2e8f35d6ef197969f1fa3d79 --- /dev/null +++ b/src/EditorFeatures/VisualBasicTest/ChangeSignature/ChangeSignatureTests.vb @@ -0,0 +1,80 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports Microsoft.CodeAnalysis.Editor.UnitTests.ChangeSignature +Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions + +Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ChangeSignature + Partial Public Class ChangeSignatureTests + Inherits AbstractChangeSignatureTests + + + + Public Async Function TestNotInLeadingWhitespace() As Task + Dim markup = " +class C + [||] + sub Foo(i as integer, j as integer) + end sub +end class +" + + Await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction:=False) + End Function + + + + Public Async Function TestNotInLeadingTrivia1() As Task + Dim markup = " +class C + ' [||] + sub Foo(i as integer, j as integer) + end sub +end class +" + + Await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction:=False) + End Function + + + + Public Async Function TestNotInLeadingTrivia2() As Task + Dim markup = " +class C + [||] ' + sub Foo(i as integer, j as integer) + end sub +end class +" + + Await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction:=False) + End Function + + + + Public Async Function TestNotInLeadingAttributes1() As Task + Dim markup = " +class C + [||] + sub Foo(i as integer, j as integer) + end sub +end class +" + + Await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction:=False) + End Function + + + + Public Async Function TestNotInLeadingAttributes2() As Task + Dim markup = " +class C + [||] + sub Foo(i as integer, j as integer) + end sub +end class +" + + Await TestChangeSignatureViaCodeActionAsync(markup, expectedCodeAction:=False) + End Function + End Class +End Namespace \ No newline at end of file diff --git a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureCodeRefactoringProvider.cs b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureCodeRefactoringProvider.cs index 6102fe75b3bea5b2a7def9922bdbbc7a681b988e..4083441de0ee490574eef7d917eecfa5ca520fd0 100644 --- a/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureCodeRefactoringProvider.cs +++ b/src/Features/Core/Portable/ChangeSignature/AbstractChangeSignatureCodeRefactoringProvider.cs @@ -13,9 +13,12 @@ internal class ChangeSignatureCodeRefactoringProvider : CodeRefactoringProvider { public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context) { - var service = context.Document.GetLanguageService(); - var actions = await service.GetChangeSignatureCodeActionAsync(context.Document, context.Span, context.CancellationToken).ConfigureAwait(false); - context.RegisterRefactorings(actions); + if (context.Span.IsEmpty) + { + var service = context.Document.GetLanguageService(); + var actions = await service.GetChangeSignatureCodeActionAsync(context.Document, context.Span, context.CancellationToken).ConfigureAwait(false); + context.RegisterRefactorings(actions); + } } } } \ No newline at end of file diff --git a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb index a576b44dbfcaf4ce0cccb451844ee354075603f9..eb0d7e9ae4fddc49885998bbf5659b86ade70e15 100644 --- a/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb +++ b/src/Features/VisualBasic/Portable/ChangeSignature/VisualBasicChangeSignatureService.vb @@ -29,8 +29,58 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Return Nothing End If - Dim semanticModel = document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken) + ' Don't show change-signature in the random whitespace/trivia for code. + If Not matchingNode.Span.IntersectsWith(position) Then + Return Nothing + End If + + Dim semanticModel = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False) + Dim symbol = TryGetDeclaredSymbol(semanticModel, matchingNode, token, cancellationToken) + If symbol IsNot Nothing Then + Return If(restrictToDeclarations AndAlso Not IsInSymbolHeader(matchingNode, position), Nothing, symbol) + End If + + If matchingNode.Kind() = SyntaxKind.ObjectCreationExpression Then + Dim objectCreation = DirectCast(matchingNode, ObjectCreationExpressionSyntax) + If token.Parent.AncestorsAndSelf().Any(Function(a) a Is objectCreation.Type) Then + Dim typeSymbol = semanticModel.GetSymbolInfo(objectCreation.Type).Symbol + If typeSymbol IsNot Nothing AndAlso typeSymbol.IsKind(SymbolKind.NamedType) AndAlso DirectCast(typeSymbol, ITypeSymbol).TypeKind = TypeKind.Delegate Then + Return typeSymbol + End If + End If + End If + + Dim symbolInfo = semanticModel.GetSymbolInfo(matchingNode, cancellationToken) + Return If(symbolInfo.Symbol, symbolInfo.CandidateSymbols.FirstOrDefault()) + End Function + + Private Function IsInSymbolHeader(matchingNode As SyntaxNode, position As Integer) As Boolean + ' Caret has to be after the attributes if the symbol has any. + Dim lastAttributes = matchingNode.ChildNodes().LastOrDefault( + Function(n) TypeOf n Is AttributeListSyntax) + Dim start = If(lastAttributes?.GetLastToken().GetNextToken().SpanStart, + matchingNode.SpanStart) + + If position < start Then + Return False + End If + + ' If the symbol has a parameter list, then the caret shouldn't be past the end of it. + Dim parameterList = matchingNode.ChildNodes().LastOrDefault( + Function(n) TypeOf n Is ParameterListSyntax) + If parameterList IsNot Nothing Then + Return position <= parameterList.FullSpan.End + End If + + ' Case we haven't handled yet. Just assume we're in the header. + Return True + End Function + + Private Function TryGetDeclaredSymbol(semanticModel As SemanticModel, + matchingNode As SyntaxNode, + token As SyntaxToken, + cancellationToken As CancellationToken) As ISymbol Select Case matchingNode.Kind() Case SyntaxKind.PropertyBlock Dim parameterList = DirectCast(matchingNode, PropertyBlockSyntax).PropertyStatement.ParameterList @@ -48,33 +98,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeSignature Return semanticModel.GetDeclaredSymbol(DirectCast(matchingNode, MethodBlockSyntax).BlockStatement, cancellationToken) Case SyntaxKind.ConstructorBlock Return semanticModel.GetDeclaredSymbol(DirectCast(matchingNode, ConstructorBlockSyntax).BlockStatement, cancellationToken) - Case SyntaxKind.ObjectCreationExpression - Dim objectCreation = DirectCast(matchingNode, ObjectCreationExpressionSyntax) - If token.Parent.AncestorsAndSelf().Any(Function(a) a Is objectCreation.Type) Then - Dim typeSymbol = semanticModel.GetSymbolInfo(objectCreation.Type).Symbol - If typeSymbol IsNot Nothing AndAlso typeSymbol.IsKind(SymbolKind.NamedType) AndAlso DirectCast(typeSymbol, ITypeSymbol).TypeKind = TypeKind.Delegate Then - Return typeSymbol - End If - End If End Select - Dim symbol = semanticModel.GetDeclaredSymbol(matchingNode, cancellationToken) - If symbol IsNot Nothing Then - Return symbol - End If - - Dim symbolInfo = semanticModel.GetSymbolInfo(matchingNode, cancellationToken) - Return If(symbolInfo.Symbol, symbolInfo.CandidateSymbols.FirstOrDefault()) + Return semanticModel.GetDeclaredSymbol(matchingNode, cancellationToken) End Function - Private _nonDeclarationKinds As ImmutableArray(Of SyntaxKind) = New List(Of SyntaxKind) From - { - SyntaxKind.SubBlock, - SyntaxKind.FunctionBlock, - SyntaxKind.PropertyBlock, - SyntaxKind.EventBlock, - SyntaxKind.ConstructorBlock - }.ToImmutableArray() + Private Shared ReadOnly _nonDeclarationKinds As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create( + SyntaxKind.SubBlock, + SyntaxKind.FunctionBlock, + SyntaxKind.PropertyBlock, + SyntaxKind.EventBlock, + SyntaxKind.ConstructorBlock) Private _invokableAncestorKinds As ImmutableArray(Of SyntaxKind) = New List(Of SyntaxKind) From {