提交 82991644 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #18235 from CyrusNajmabadi/replacePropertyDocComments

Properly preserve doc comments when converting a property into a method.
......@@ -1325,6 +1325,97 @@ private int GetProp()
}", ignoreTrivia: false, options: PreferExpressionBodiedMethods);
}
[WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestDocumentationComment1()
{
await TestInRegularAndScriptAsync(
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <value>
/// An value that provides access to the language service for the active configured project.
/// </value>
object [||]ActiveProjectContext
{
get;
}
}",
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <returns>
/// An value that provides access to the language service for the active configured project.
/// </returns>
object GetActiveProjectContext();
}", ignoreTrivia: false);
}
[WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestDocumentationComment2()
{
await TestInRegularAndScriptAsync(
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <value>
/// An value that provides access to the language service for the active configured project.
/// </value>
object [||]ActiveProjectContext
{
set;
}
}",
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <returns>
/// An value that provides access to the language service for the active configured project.
/// </returns>
void SetActiveProjectContext(object value);
}", ignoreTrivia: false);
}
[WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestDocumentationComment3()
{
await TestInRegularAndScriptAsync(
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <value>
/// An value that provides access to the language service for the active configured project.
/// </value>
object [||]ActiveProjectContext
{
get; set;
}
}",
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <returns>
/// An value that provides access to the language service for the active configured project.
/// </returns>
object GetActiveProjectContext();
void SetActiveProjectContext(object value);
}", ignoreTrivia: false);
}
private IDictionary<OptionKey, object> PreferExpressionBodiedMethods =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement));
}
......
......@@ -414,5 +414,78 @@ end class",
End Function
end class")
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment1() As Task
Await TestInRegularAndScriptAsync(
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <value>
''' An that provides access to the language service for the active configured project.
''' </value>
ReadOnly Property [||]ActiveProjectContext As Object
End Interface",
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <returns>
''' An that provides access to the language service for the active configured project.
''' </returns>
Function GetActiveProjectContext() As Object
End Interface", ignoreTrivia:=False)
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment2() As Task
Await TestInRegularAndScriptAsync(
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <value>
''' An that provides access to the language service for the active configured project.
''' </value>
WriteOnly Property [||]ActiveProjectContext As Object
End Interface",
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <returns>
''' An that provides access to the language service for the active configured project.
''' </returns>
Sub SetActiveProjectContext(Value As Object)
End Interface", ignoreTrivia:=False)
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment3() As Task
Await TestInRegularAndScriptAsync(
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <value>
''' An that provides access to the language service for the active configured project.
''' </value>
Property [||]ActiveProjectContext As Object
End Interface",
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <returns>
''' An that provides access to the language service for the active configured project.
''' </returns>
Function GetActiveProjectContext() As Object
Sub SetActiveProjectContext(Value As Object)
End Interface", ignoreTrivia:=False)
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -81,6 +81,7 @@
<Compile Include="Completion\CompletionProviders\DeclarationNameCompletionProvider.cs" />
<Compile Include="ImplementAbstractClass\CSharpImplementAbstractClassCodeFixProvider.cs" />
<Compile Include="ImplementInterface\CSharpImplementInterfaceCodeFixProvider.cs" />
<Compile Include="ReplacePropertyWithMethods\ConvertValueToReturnsRewriter.cs" />
<Compile Include="Structure\Providers\ArrowExpressionClauseStructureProvider.cs" />
<Compile Include="ConvertToInterpolatedString\CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="RemoveUnnecessaryImports\CSharpRemoveUnnecessaryImportsService.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Composition;
using System.Linq;
......@@ -19,7 +20,7 @@
namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods
{
[ExportLanguageService(typeof(IReplacePropertyWithMethodsService), LanguageNames.CSharp), Shared]
internal class CSharpReplacePropertyWithMethodsService :
internal partial class CSharpReplacePropertyWithMethodsService :
AbstractReplacePropertyWithMethodsService<IdentifierNameSyntax, ExpressionSyntax, StatementSyntax>
{
public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
......@@ -96,16 +97,22 @@ public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
result.Add(GetGetMethod(
documentOptions, parseOptions,
generator, propertyDeclaration, propertyBackingField,
getMethod, desiredGetMethodName, cancellationToken));
getMethod, desiredGetMethodName,
copyLeadingTrivia: true,
cancellationToken: cancellationToken));
}
var setMethod = property.SetMethod;
if (setMethod != null)
{
// Set-method only gets the leading trivia of the property if we didn't copy
// that trivia to the get-method.
result.Add(GetSetMethod(
documentOptions, parseOptions,
generator, propertyDeclaration, propertyBackingField,
setMethod, desiredSetMethodName, cancellationToken));
setMethod, desiredSetMethodName,
copyLeadingTrivia: getMethod == null,
cancellationToken: cancellationToken));
}
return result;
......@@ -119,12 +126,15 @@ public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
IFieldSymbol propertyBackingField,
IMethodSymbol setMethod,
string desiredSetMethodName,
bool copyLeadingTrivia,
CancellationToken cancellationToken)
{
var methodDeclaration = GetSetMethodWorker(
generator, propertyDeclaration, propertyBackingField,
setMethod, desiredSetMethodName, cancellationToken);
methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, copyLeadingTrivia);
return UseExpressionOrBlockBodyIfDesired(
documentOptions, parseOptions, methodDeclaration,
createReturnStatementForExpression: false);
......@@ -172,16 +182,52 @@ public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
IFieldSymbol propertyBackingField,
IMethodSymbol getMethod,
string desiredGetMethodName,
bool copyLeadingTrivia,
CancellationToken cancellationToken)
{
var methodDeclaration = GetGetMethodWorker(
generator, propertyDeclaration, propertyBackingField, getMethod,
desiredGetMethodName, cancellationToken);
methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, copyLeadingTrivia);
return UseExpressionOrBlockBodyIfDesired(
documentOptions, parseOptions, methodDeclaration,
createReturnStatementForExpression: true);
}
private static MethodDeclarationSyntax CopyLeadingTrivia(
PropertyDeclarationSyntax propertyDeclaration,
MethodDeclarationSyntax methodDeclaration,
bool copyLeadingTrivia)
{
if (copyLeadingTrivia)
{
var leadingTrivia = propertyDeclaration.GetLeadingTrivia();
return methodDeclaration.WithLeadingTrivia(leadingTrivia.Select(ConvertTrivia));
}
return methodDeclaration;
}
private static SyntaxTrivia ConvertTrivia(SyntaxTrivia trivia)
{
if (trivia.Kind() == SyntaxKind.MultiLineDocumentationCommentTrivia ||
trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
return ConvertDocumentationComment(trivia);
}
return trivia;
}
private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia)
{
var structure = trivia.GetStructure();
var updatedStructure = (StructuredTriviaSyntax)ConvertValueToReturnsRewriter.Instance.Visit(structure);
return SyntaxFactory.Trivia(updatedStructure);
}
private static SyntaxNode UseExpressionOrBlockBodyIfDesired(
DocumentOptionSet documentOptions, ParseOptions parseOptions,
MethodDeclarationSyntax methodDeclaration, bool createReturnStatementForExpression)
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods
{
internal partial class CSharpReplacePropertyWithMethodsService
{
private class ConvertValueToReturnsRewriter : CSharpSyntaxRewriter
{
public static readonly CSharpSyntaxRewriter Instance = new ConvertValueToReturnsRewriter();
private ConvertValueToReturnsRewriter()
{
}
private XmlNameSyntax ConvertToReturns(XmlNameSyntax name)
=> name.ReplaceToken(name.LocalName, SyntaxFactory.Identifier("returns"));
private static bool IsValueName(XmlNameSyntax name)
=> name.Prefix == null &&
name.LocalName.ValueText == "value";
public override SyntaxNode VisitXmlElementStartTag(XmlElementStartTagSyntax node)
=> IsValueName(node.Name)
? node.ReplaceNode(node.Name, ConvertToReturns(node.Name))
: base.VisitXmlElementStartTag(node);
public override SyntaxNode VisitXmlElementEndTag(XmlElementEndTagSyntax node)
=> IsValueName(node.Name)
? node.ReplaceNode(node.Name, ConvertToReturns(node.Name))
: base.VisitXmlElementEndTag(node);
}
}
}
\ No newline at end of file
......@@ -107,6 +107,7 @@
<Compile Include="CodeFixes\OverloadBase\OverloadBaseCodeFixProvider.AddOverloads.vb" />
<Compile Include="CodeFixes\OverloadBase\OverloadBaseCodeFixProvider.vb" />
<Compile Include="ImplementInterface\VisualBasicImplementInterfaceCodeFixProvider.vb" />
<Compile Include="ReplacePropertyWithMethods\ConvertValueToReturnsRewriter.vb" />
<Compile Include="Structure\Providers\CollectionInitializerStructureProvider.vb" />
<Compile Include="Structure\Providers\ObjectCreationInitializerStructureProvider.vb" />
<Compile Include="QualifyMemberAccess\VisualBasicQualifyMemberAccessCodeFixProvider.vb" />
......
' 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.ReplacePropertyWithMethods
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty
Partial Friend Class VisualBasicReplacePropertyWithMethods
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax)
Private Class ConvertValueToReturnsRewriter
Inherits VisualBasicSyntaxRewriter
Public Shared ReadOnly instance As New ConvertValueToReturnsRewriter()
Private Sub New()
End Sub
Private Function IsValueName(node As XmlNodeSyntax) As Boolean
Dim name = TryCast(node, XmlNameSyntax)
Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value"
End Function
Private Function ConvertToReturns(name As XmlNodeSyntax) As SyntaxNode
Return name.ReplaceToken(DirectCast(name, XmlNameSyntax).LocalName,
SyntaxFactory.XmlNameToken("returns", SyntaxKind.IdentifierToken))
End Function
Public Overrides Function VisitXmlElementStartTag(node As XmlElementStartTagSyntax) As SyntaxNode
Return If(IsValueName(node.Name),
node.ReplaceNode(node.Name, ConvertToReturns(node.Name)),
MyBase.VisitXmlElementStartTag(node))
End Function
Public Overrides Function VisitXmlElementEndTag(node As XmlElementEndTagSyntax) As SyntaxNode
Return If(IsValueName(node.Name),
node.ReplaceNode(node.Name, ConvertToReturns(node.Name)),
MyBase.VisitXmlElementEndTag(node))
End Function
End Class
End Class
End Namespace
\ No newline at end of file
......@@ -10,7 +10,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty
<ExportLanguageService(GetType(IReplacePropertyWithMethodsService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicReplacePropertyWithMethods
Partial Friend Class VisualBasicReplacePropertyWithMethods
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax)
Public Overrides Function GetPropertyDeclaration(token As SyntaxToken) As SyntaxNode
......@@ -83,14 +83,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
If getMethod IsNot Nothing Then
result.Add(GetGetMethod(
generator, propertyStatement, propertyBackingField,
getMethod, desiredGetMethodName, cancellationToken))
getMethod, desiredGetMethodName,
copyLeadingTrivia:=True,
cancellationToken:=cancellationToken))
End If
Dim setMethod = [property].SetMethod
If setMethod IsNot Nothing Then
result.Add(GetSetMethod(
generator, propertyStatement, propertyBackingField,
setMethod, desiredSetMethodName, cancellationToken))
setMethod, desiredSetMethodName,
copyLeadingTrivia:=getMethod Is Nothing,
cancellationToken:=cancellationToken))
End If
Return result
......@@ -102,6 +106,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
propertyBackingField As IFieldSymbol,
getMethod As IMethodSymbol,
desiredGetMethodName As String,
copyLeadingTrivia As Boolean,
cancellationToken As CancellationToken) As SyntaxNode
Dim statements = New List(Of SyntaxNode)()
......@@ -117,7 +122,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
statements.Add(generator.ReturnStatement(fieldReference))
End If
Return generator.MethodDeclaration(getMethod, desiredGetMethodName, statements)
Dim methodDeclaration = generator.MethodDeclaration(getMethod, desiredGetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia)
Return methodDeclaration
End Function
Private Shared Function WithFormattingAnnotation(statement As StatementSyntax) As StatementSyntax
......@@ -130,6 +137,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
propertyBackingField As IFieldSymbol,
setMethod As IMethodSymbol,
desiredSetMethodName As String,
copyLeadingTrivia As Boolean,
cancellationToken As CancellationToken) As SyntaxNode
Dim statements = New List(Of SyntaxNode)()
......@@ -146,7 +154,29 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
fieldReference, generator.IdentifierName(setMethod.Parameters(0).Name)))
End If
Return generator.MethodDeclaration(setMethod, desiredSetMethodName, statements)
Dim methodDeclaration = generator.MethodDeclaration(setMethod, desiredSetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia)
Return methodDeclaration
End Function
Private Function CopyLeadingTriviaOver(propertyStatement As PropertyStatementSyntax,
methodDeclaration As SyntaxNode,
copyLeadingTrivia As Boolean) As SyntaxNode
If copyLeadingTrivia Then
Return methodDeclaration.WithLeadingTrivia(
propertyStatement.GetLeadingTrivia().Select(AddressOf ConvertTrivia))
End If
Return methodDeclaration
End Function
Private Function ConvertTrivia(trivia As SyntaxTrivia) As SyntaxTrivia
If trivia.Kind() = SyntaxKind.DocumentationCommentTrivia Then
Dim converted = ConvertValueToReturnsRewriter.instance.Visit(trivia.GetStructure())
Return SyntaxFactory.Trivia(DirectCast(converted, StructuredTriviaSyntax))
End If
Return trivia
End Function
Public Overrides Function GetPropertyNodeToReplace(propertyDeclaration As SyntaxNode) As SyntaxNode
......
......@@ -1317,17 +1317,21 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Return AsInterfaceMember(DirectCast(node, MethodBlockSyntax).BlockStatement)
Case SyntaxKind.FunctionStatement,
SyntaxKind.SubStatement
Return DirectCast(node, MethodStatementSyntax).WithModifiers(Nothing)
Return Isolate(node, Function(d) DirectCast(d, MethodStatementSyntax).WithModifiers(Nothing))
Case SyntaxKind.PropertyBlock
Return AsInterfaceMember(DirectCast(node, PropertyBlockSyntax).PropertyStatement)
Case SyntaxKind.PropertyStatement
Dim propertyStatement = DirectCast(node, PropertyStatementSyntax)
Dim mods = SyntaxFactory.TokenList(propertyStatement.Modifiers.Where(Function(tk) tk.IsKind(SyntaxKind.ReadOnlyKeyword) Or tk.IsKind(SyntaxKind.DefaultKeyword)))
Return propertyStatement.WithModifiers(mods)
Return Isolate(
node,
Function(d)
Dim propertyStatement = DirectCast(d, PropertyStatementSyntax)
Dim mods = SyntaxFactory.TokenList(propertyStatement.Modifiers.Where(Function(tk) tk.IsKind(SyntaxKind.ReadOnlyKeyword) Or tk.IsKind(SyntaxKind.DefaultKeyword)))
Return propertyStatement.WithModifiers(mods)
End Function)
Case SyntaxKind.EventBlock
Return AsInterfaceMember(DirectCast(node, EventBlockSyntax).EventStatement)
Case SyntaxKind.EventStatement
Return DirectCast(node, EventStatementSyntax).WithModifiers(Nothing).WithCustomKeyword(Nothing)
Return Isolate(node, Function(d) DirectCast(d, EventStatementSyntax).WithModifiers(Nothing).WithCustomKeyword(Nothing))
End Select
End If
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册