提交 2f23f3b0 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #18243 from sharwell/document-setters

Improve support for documentation comments in Replace Property with Methods
......@@ -1363,7 +1363,7 @@ public async Task TestDocumentationComment2()
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// Sets 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.
......@@ -1376,11 +1376,11 @@ public async Task TestDocumentationComment2()
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// Sets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <returns>
/// <param name=""value"">
/// An value that provides access to the language service for the active configured project.
/// </returns>
/// </param>
void SetActiveProjectContext(object value);
}", ignoreTrivia: false);
}
......@@ -1393,7 +1393,7 @@ public async Task TestDocumentationComment3()
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// Gets or sets 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.
......@@ -1406,13 +1406,128 @@ public async Task TestDocumentationComment3()
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets the active workspace project context that provides access to the language service for the active configured project.
/// Gets or sets 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();
/// <summary>
/// Gets or sets the active workspace project context that provides access to the language service for the active configured project.
/// </summary>
/// <param name=""value"">
/// An value that provides access to the language service for the active configured project.
/// </param>
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 TestDocumentationComment4()
{
await TestInRegularAndScriptAsync(
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Sets <see cref=""ActiveProjectContext""/>.
/// </summary>
/// <seealso cref=""ActiveProjectContext""/>
object [||]ActiveProjectContext
{
set;
}
}
internal struct AStruct
{
/// <seealso cref=""ILanguageServiceHost.ActiveProjectContext""/>
private int x;
}",
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Sets <see cref=""SetActiveProjectContext(object)""/>.
/// </summary>
/// <seealso cref=""SetActiveProjectContext(object)""/>
void SetActiveProjectContext(object value);
}
internal struct AStruct
{
/// <seealso cref=""ILanguageServiceHost.SetActiveProjectContext(object)""/>
private int x;
}", ignoreTrivia: false);
}
[WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestDocumentationComment5()
{
await TestInRegularAndScriptAsync(
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets or sets <see cref=""ActiveProjectContext""/>.
/// </summary>
/// <seealso cref=""ActiveProjectContext""/>
object [||]ActiveProjectContext
{
get; set;
}
}
internal struct AStruct
{
/// <seealso cref=""ILanguageServiceHost.ActiveProjectContext""/>
private int x;
}",
@"internal interface ILanguageServiceHost
{
/// <summary>
/// Gets or sets <see cref=""GetActiveProjectContext()""/>.
/// </summary>
/// <seealso cref=""GetActiveProjectContext()""/>
object GetActiveProjectContext();
/// <summary>
/// Gets or sets <see cref=""GetActiveProjectContext()""/>.
/// </summary>
/// <seealso cref=""GetActiveProjectContext()""/>
void SetActiveProjectContext(object value);
}
internal struct AStruct
{
/// <seealso cref=""ILanguageServiceHost.GetActiveProjectContext()""/>
private int x;
}", ignoreTrivia: false);
}
[WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)]
public async Task TestDocumentationComment6()
{
await TestInRegularAndScriptAsync(
@"internal interface ISomeInterface<T>
{
/// <seealso cref=""Context""/>
ISomeInterface<T> [||]Context
{
set;
}
}
internal struct AStruct
{
/// <seealso cref=""ISomeInterface{T}.Context""/>
private int x;
}",
@"internal interface ISomeInterface<T>
{
/// <seealso cref=""SetContext(ISomeInterface{T})""/>
void SetContext(ISomeInterface<T> value);
}
internal struct AStruct
{
/// <seealso cref=""ISomeInterface{T}.SetContext(ISomeInterface{T})""/>
private int x;
}", ignoreTrivia: false);
}
......@@ -1533,4 +1648,4 @@ public async Task TestWithDirectives4()
private IDictionary<OptionKey, object> PreferExpressionBodiedMethods =>
OptionsSet(SingleOption(CSharpCodeStyleOptions.PreferExpressionBodiedMethods, CSharpCodeStyleOptions.WhenPossibleWithSuggestionEnforcement));
}
}
\ No newline at end of file
}
......@@ -415,7 +415,7 @@ end class",
end class")
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment1() As Task
Await TestInRegularAndScriptAsync(
......@@ -439,13 +439,13 @@ End Interface",
End Interface", ignoreTrivia:=False)
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<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.
''' Sets 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.
......@@ -454,22 +454,22 @@ End Interface", ignoreTrivia:=False)
End Interface",
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' Sets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <returns>
''' <param name=""Value"">
''' An that provides access to the language service for the active configured project.
''' </returns>
''' </param>
Sub SetActiveProjectContext(Value As Object)
End Interface", ignoreTrivia:=False)
End Function
<WorkItem(18235, "https://github.com/dotnet/roslyn/pull/18235")>
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<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.
''' Gets or sets 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.
......@@ -478,14 +478,103 @@ End Interface", ignoreTrivia:=False)
End Interface",
"Interface ILanguageServiceHost
''' <summary>
''' Gets the active workspace project context that provides access to the language service for the active configured project.
''' Gets or sets 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
''' <summary>
''' Gets or sets the active workspace project context that provides access to the language service for the active configured project.
''' </summary>
''' <param name=""Value"">
''' An that provides access to the language service for the active configured project.
''' </param>
Sub SetActiveProjectContext(Value As Object)
End Interface", ignoreTrivia:=False)
End Function
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment4() As Task
Await TestInRegularAndScriptAsync(
"Interface ILanguageServiceHost
''' <summary>
''' Sets <see cref=""ActiveProjectContext""/>.
''' </summary>
''' <seealso cref=""ActiveProjectContext""/>
WriteOnly Property [||]ActiveProjectContext As Object
End Interface
Structure AStruct
''' <seealso cref=""ILanguageServiceHost.ActiveProjectContext""/>
Private X As Integer
End Structure",
"Interface ILanguageServiceHost
''' <summary>
''' Sets <see cref=""SetActiveProjectContext(Object)""/>.
''' </summary>
''' <seealso cref=""SetActiveProjectContext(Object)""/>
Sub SetActiveProjectContext(Value As Object)
End Interface
Structure AStruct
''' <seealso cref=""ILanguageServiceHost.SetActiveProjectContext(Object)""/>
Private X As Integer
End Structure", ignoreTrivia:=False)
End Function
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment5() As Task
Await TestInRegularAndScriptAsync(
"Interface ILanguageServiceHost
''' <summary>
''' Gets or sets <see cref=""ActiveProjectContext""/>.
''' </summary>
''' <seealso cref=""ActiveProjectContext""/>
Property [||]ActiveProjectContext As Object
End Interface
Structure AStruct
''' <seealso cref=""ILanguageServiceHost.ActiveProjectContext""/>
Private X As Integer
End Structure",
"Interface ILanguageServiceHost
''' <summary>
''' Gets or sets <see cref=""GetActiveProjectContext()""/>.
''' </summary>
''' <seealso cref=""GetActiveProjectContext()""/>
Function GetActiveProjectContext() As Object
''' <summary>
''' Gets or sets <see cref=""GetActiveProjectContext()""/>.
''' </summary>
''' <seealso cref=""GetActiveProjectContext()""/>
Sub SetActiveProjectContext(Value As Object)
End Interface
Structure AStruct
''' <seealso cref=""ILanguageServiceHost.GetActiveProjectContext()""/>
Private X As Integer
End Structure", ignoreTrivia:=False)
End Function
<WorkItem(18234, "https://github.com/dotnet/roslyn/issues/18234")>
<Fact(Skip:="https://github.com/dotnet/roslyn/issues/18261"), Trait(Traits.Feature, Traits.Features.CodeActionsReplacePropertyWithMethods)>
Public Async Function TestDocumentationComment6() As Task
Await TestInRegularAndScriptAsync(
"Interface ISomeInterface(Of T)
''' <seealso cref=""Context""/>
WriteOnly Property [||]Context As ISomeInterface(Of T)
End Interface
Structure AStruct
''' <seealso cref=""ISomeInterface(Of T).Context""/>
Private X As Integer
End Structure",
"Interface ISomeInterface(Of T)
''' <seealso cref=""SetContext(ISomeInterface(Of T))""/>
Sub SetContext(Value As ISomeInterface(Of T))
End Interface
Structure AStruct
''' <seealso cref=""ISomeInterface(Of T).SetContext(ISomeInterface(Of T))""/>
Private X As Integer
End Structure", ignoreTrivia:=False)
End Function
End Class
End Namespace
\ No newline at end of file
......@@ -86,7 +86,6 @@
<Compile Include="InitializeParameter\CSharpAddParameterCheckCodeRefactoringProvider.cs" />
<Compile Include="InitializeParameter\CSharpInitializeMemberFromParameterCodeRefactoringProvider.cs" />
<Compile Include="InitializeParameter\InitializeParameterHelpers.cs" />
<Compile Include="ReplacePropertyWithMethods\ConvertValueToReturnsRewriter.cs" />
<Compile Include="Structure\Providers\ArrowExpressionClauseStructureProvider.cs" />
<Compile Include="ConvertToInterpolatedString\CSharpConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="RemoveUnnecessaryImports\CSharpRemoveUnnecessaryImportsService.cs" />
......@@ -450,6 +449,8 @@
<Compile Include="RemoveUnnecessaryImports\AbstractCSharpRemoveUnnecessaryImportsService.cs" />
<Compile Include="RemoveUnnecessaryImports\AbstractCSharpRemoveUnnecessaryImportsService.Rewriter.cs" />
<Compile Include="ReplaceMethodWithProperty\CSharpReplaceMethodWithPropertyService.cs" />
<Compile Include="ReplacePropertyWithMethods\CSharpReplacePropertyWithMethodsService.ConvertValueToParamRewriter.cs" />
<Compile Include="ReplacePropertyWithMethods\CSharpReplacePropertyWithMethodsService.ConvertValueToReturnsRewriter.cs" />
<Compile Include="ReplacePropertyWithMethods\CSharpReplacePropertyWithMethodsService.cs" />
<Compile Include="SignatureHelp\AbstractCSharpSignatureHelpProvider.cs" />
<Compile Include="SignatureHelp\AttributeSignatureHelpProvider.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 Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods
{
internal partial class CSharpReplacePropertyWithMethodsService
{
private class ConvertValueToParamRewriter : CSharpSyntaxRewriter
{
public static readonly CSharpSyntaxRewriter Instance = new ConvertValueToParamRewriter();
private ConvertValueToParamRewriter()
{
}
private XmlNameSyntax ConvertToParam(XmlNameSyntax name)
=> name.ReplaceToken(name.LocalName, SyntaxFactory.Identifier("param"));
public override SyntaxNode VisitXmlElementStartTag(XmlElementStartTagSyntax node)
{
if (!IsValueName(node.Name))
return base.VisitXmlElementStartTag(node);
return node.ReplaceNode(node.Name, ConvertToParam(node.Name))
.AddAttributes(SyntaxFactory.XmlNameAttribute("value"));
}
public override SyntaxNode VisitXmlElementEndTag(XmlElementEndTagSyntax node)
=> IsValueName(node.Name)
? node.ReplaceNode(node.Name, ConvertToParam(node.Name))
: base.VisitXmlElementEndTag(node);
}
}
}
......@@ -17,10 +17,6 @@ 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))
......
......@@ -21,7 +21,7 @@ namespace Microsoft.CodeAnalysis.CSharp.ReplacePropertyWithMethods
{
[ExportLanguageService(typeof(IReplacePropertyWithMethodsService), LanguageNames.CSharp), Shared]
internal partial class CSharpReplacePropertyWithMethodsService :
AbstractReplacePropertyWithMethodsService<IdentifierNameSyntax, ExpressionSyntax, StatementSyntax>
AbstractReplacePropertyWithMethodsService<IdentifierNameSyntax, ExpressionSyntax, NameMemberCrefSyntax, StatementSyntax>
{
public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
{
......@@ -98,20 +98,16 @@ public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
documentOptions, parseOptions,
generator, propertyDeclaration, propertyBackingField,
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,
copyLeadingTrivia: getMethod == null,
cancellationToken: cancellationToken));
}
......@@ -126,14 +122,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);
// The analyzer doesn't report diagnostics when the trivia contains preprocessor directives, so it's safe
// to copy the complete leading trivia to both generated methods.
methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, ConvertValueToParamRewriter.Instance);
return UseExpressionOrBlockBodyIfDesired(
documentOptions, parseOptions, methodDeclaration,
......@@ -182,14 +179,13 @@ 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);
methodDeclaration = CopyLeadingTrivia(propertyDeclaration, methodDeclaration, ConvertValueToReturnsRewriter.Instance);
return UseExpressionOrBlockBodyIfDesired(
documentOptions, parseOptions, methodDeclaration,
......@@ -199,32 +195,27 @@ public override SyntaxNode GetPropertyDeclaration(SyntaxToken token)
private static MethodDeclarationSyntax CopyLeadingTrivia(
PropertyDeclarationSyntax propertyDeclaration,
MethodDeclarationSyntax methodDeclaration,
bool copyLeadingTrivia)
CSharpSyntaxRewriter documentationCommentRewriter)
{
if (copyLeadingTrivia)
{
var leadingTrivia = propertyDeclaration.GetLeadingTrivia();
return methodDeclaration.WithLeadingTrivia(leadingTrivia.Select(ConvertTrivia));
}
return methodDeclaration;
var leadingTrivia = propertyDeclaration.GetLeadingTrivia();
return methodDeclaration.WithLeadingTrivia(leadingTrivia.Select(trivia => ConvertTrivia(trivia, documentationCommentRewriter)));
}
private static SyntaxTrivia ConvertTrivia(SyntaxTrivia trivia)
private static SyntaxTrivia ConvertTrivia(SyntaxTrivia trivia, CSharpSyntaxRewriter rewriter)
{
if (trivia.Kind() == SyntaxKind.MultiLineDocumentationCommentTrivia ||
trivia.Kind() == SyntaxKind.SingleLineDocumentationCommentTrivia)
{
return ConvertDocumentationComment(trivia);
return ConvertDocumentationComment(trivia, rewriter);
}
return trivia;
}
private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia)
private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia, CSharpSyntaxRewriter rewriter)
{
var structure = trivia.GetStructure();
var updatedStructure = (StructuredTriviaSyntax)ConvertValueToReturnsRewriter.Instance.Visit(structure);
var updatedStructure = (StructuredTriviaSyntax)rewriter.Visit(structure);
return SyntaxFactory.Trivia(updatedStructure);
}
......@@ -301,12 +292,41 @@ private static SyntaxTrivia ConvertDocumentationComment(SyntaxTrivia trivia)
return methodDeclaration;
}
/// <summary>
/// Used by the documentation comment rewriters to identify top-level <c>&lt;value&gt;</c> nodes.
/// </summary>
private static bool IsValueName(XmlNameSyntax name)
=> name.Prefix == null &&
name.LocalName.ValueText == "value";
public override SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclaration)
{
// For C# we'll have the property declaration that we want to replace.
return propertyDeclaration;
}
protected override NameMemberCrefSyntax TryGetCrefSyntax(IdentifierNameSyntax identifierName)
=> identifierName.Parent as NameMemberCrefSyntax;
protected override NameMemberCrefSyntax CreateCrefSyntax(NameMemberCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode parameterType)
{
CrefParameterListSyntax parameterList;
if (parameterType is TypeSyntax typeSyntax)
{
var parameter = SyntaxFactory.CrefParameter(typeSyntax);
parameterList = SyntaxFactory.CrefParameterList(SyntaxFactory.SingletonSeparatedList(parameter));
}
else
{
parameterList = SyntaxFactory.CrefParameterList();
}
// XmlCrefAttribute replaces <T> with {T}, which is required for C# documentation comments
var crefAttribute = SyntaxFactory.XmlCrefAttribute(
SyntaxFactory.NameMemberCref(SyntaxFactory.IdentifierName(identifierToken), parameterList));
return (NameMemberCrefSyntax)crefAttribute.Cref;
}
protected override ExpressionSyntax UnwrapCompoundAssignment(
SyntaxNode compoundAssignment, ExpressionSyntax readExpression)
{
......
......@@ -12,16 +12,20 @@
namespace Microsoft.CodeAnalysis.ReplacePropertyWithMethods
{
internal abstract class AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TStatementSyntax>
internal abstract class AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TCrefSyntax, TStatementSyntax>
: IReplacePropertyWithMethodsService
where TIdentifierNameSyntax : TExpressionSyntax
where TExpressionSyntax : SyntaxNode
where TCrefSyntax : SyntaxNode
where TStatementSyntax : SyntaxNode
{
public abstract SyntaxNode GetPropertyDeclaration(SyntaxToken token);
public abstract SyntaxNode GetPropertyNodeToReplace(SyntaxNode propertyDeclaration);
public abstract Task<IList<SyntaxNode>> GetReplacementMembersAsync(Document document, IPropertySymbol property, SyntaxNode propertyDeclaration, IFieldSymbol propertyBackingField, string desiredGetMethodName, string desiredSetMethodName, CancellationToken cancellationToken);
protected abstract TCrefSyntax TryGetCrefSyntax(TIdentifierNameSyntax identifierName);
protected abstract TCrefSyntax CreateCrefSyntax(TCrefSyntax originalCref, SyntaxToken identifierToken, SyntaxNode parameterType);
protected abstract TExpressionSyntax UnwrapCompoundAssignment(SyntaxNode compoundAssignment, TExpressionSyntax readExpression);
protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldSymbol propertyBackingField)
......@@ -62,7 +66,7 @@ protected static SyntaxNode GetFieldReference(SyntaxGenerator generator, IFieldS
private struct ReferenceReplacer
{
private readonly AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TStatementSyntax> _service;
private readonly AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TCrefSyntax, TStatementSyntax> _service;
private readonly SemanticModel _semanticModel;
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
......@@ -75,10 +79,11 @@ private struct ReferenceReplacer
private readonly TIdentifierNameSyntax _identifierName;
private readonly TExpressionSyntax _expression;
private readonly TCrefSyntax _cref;
private readonly CancellationToken _cancellationToken;
public ReferenceReplacer(
AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TStatementSyntax> service,
AbstractReplacePropertyWithMethodsService<TIdentifierNameSyntax, TExpressionSyntax, TCrefSyntax, TStatementSyntax> service,
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
......@@ -102,6 +107,7 @@ private struct ReferenceReplacer
_identifierName = (TIdentifierNameSyntax)nameToken.Parent;
_expression = _identifierName;
_cref = _service.TryGetCrefSyntax(_identifierName);
if (_syntaxFacts.IsNameOfMemberAccessExpression(_expression))
{
_expression = _expression.Parent as TExpressionSyntax;
......@@ -187,7 +193,13 @@ private struct ReferenceReplacer
public void Do()
{
if (_semanticFacts.IsInOutContext(_semanticModel, _expression, _cancellationToken) ||
if (_cref != null)
{
// We're in a documentation comment. Replace with a reference to the getter if one exists,
// otherwise to the setter.
_editor.ReplaceNode(_cref, GetCrefReference(_cref));
}
else if (_semanticFacts.IsInOutContext(_semanticModel, _expression, _cancellationToken) ||
_semanticFacts.IsInRefContext(_semanticModel, _expression, _cancellationToken))
{
// Code wasn't legal (you can't reference a property in an out/ref position in C#).
......@@ -267,6 +279,24 @@ private void ReplaceRead(bool keepTrivia, string conflictMessage)
new ReplaceParentArgs(this, getWriteValue, keepTrivia, conflictMessage));
}
private TCrefSyntax GetCrefReference(TCrefSyntax originalCref)
{
SyntaxToken newIdentifierToken;
SyntaxNode parameterType;
if (_property.GetMethod != null)
{
newIdentifierToken = Generator.Identifier(_desiredGetMethodName);
parameterType = null;
}
else
{
newIdentifierToken = Generator.Identifier(_desiredSetMethodName);
parameterType = Generator.TypeExpression(_property.Type);
}
return _service.CreateCrefSyntax(originalCref, newIdentifierToken, parameterType);
}
private TExpressionSyntax GetReadExpression(
bool keepTrivia, string conflictMessage)
{
......
......@@ -245,7 +245,7 @@ private string GetDefinitionIssues(IEnumerable<ReferencedSymbol> getMethodRefere
var property = tuple.property;
var referenceLocation = tuple.location;
var location = referenceLocation.Location;
var nameToken = root.FindToken(location.SourceSpan.Start);
var nameToken = root.FindToken(location.SourceSpan.Start, findInsideTrivia: true);
if (referenceLocation.IsImplicit)
{
......
......@@ -110,7 +110,6 @@
<Compile Include="CodeFixes\OverloadBase\OverloadBaseCodeFixProvider.vb" />
<Compile Include="ImplementInterface\VisualBasicImplementInterfaceCodeFixProvider.vb" />
<Compile Include="InitializeParameter\VisualBasicInitializeMemberFromParameterCodeRefactoringProvider.vb" />
<Compile Include="ReplacePropertyWithMethods\ConvertValueToReturnsRewriter.vb" />
<Compile Include="Structure\Providers\CollectionInitializerStructureProvider.vb" />
<Compile Include="Structure\Providers\ObjectCreationInitializerStructureProvider.vb" />
<Compile Include="QualifyMemberAccess\VisualBasicQualifyMemberAccessCodeFixProvider.vb" />
......@@ -396,6 +395,8 @@
<Compile Include="Structure\VisualBasicBlockStructureProvider.vb" />
<Compile Include="RemoveUnnecessaryImports\AbstractVisualBasicRemoveUnnecessaryImportsService.Rewriter.vb" />
<Compile Include="RemoveUnnecessaryImports\AbstractVisualBasicRemoveUnnecessaryImportsService.vb" />
<Compile Include="ReplacePropertyWithMethods\VisualBasicReplacePropertyWithMethods.ConvertValueToParamRewriter.vb" />
<Compile Include="ReplacePropertyWithMethods\VisualBasicReplacePropertyWithMethods.ConvertValueToReturnsRewriter.vb" />
<Compile Include="ReplacePropertyWithMethods\VisualBasicReplacePropertyWithMethods.vb" />
<Compile Include="SignatureHelp\AbstractIntrinsicOperatorSignatureHelpProvider.vb" />
<Compile Include="SignatureHelp\AbstractSignatureHelpProvider.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, CrefReferenceSyntax, StatementSyntax)
Private Class ConvertValueToParamRewriter
Inherits VisualBasicSyntaxRewriter
Public Shared ReadOnly instance As New ConvertValueToParamRewriter()
Private Sub New()
End Sub
Private Function ConvertToParam(name As XmlNodeSyntax) As SyntaxNode
Return name.ReplaceToken(DirectCast(name, XmlNameSyntax).LocalName,
SyntaxFactory.XmlNameToken("param", SyntaxKind.IdentifierToken))
End Function
Public Overrides Function VisitXmlElementStartTag(node As XmlElementStartTagSyntax) As SyntaxNode
If Not IsValueName(node.Name) Then
Return MyBase.VisitXmlElementStartTag(node)
End If
Return node.ReplaceNode(node.Name, ConvertToParam(node.Name)) _
.AddAttributes(SyntaxFactory.XmlNameAttribute("Value"))
End Function
Public Overrides Function VisitXmlElementEndTag(node As XmlElementEndTagSyntax) As SyntaxNode
Return If(IsValueName(node.Name),
node.ReplaceNode(node.Name, ConvertToParam(node.Name)),
MyBase.VisitXmlElementEndTag(node))
End Function
End Class
End Class
End Namespace
......@@ -5,7 +5,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty
Partial Friend Class VisualBasicReplacePropertyWithMethods
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax)
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, CrefReferenceSyntax, StatementSyntax)
Private Class ConvertValueToReturnsRewriter
Inherits VisualBasicSyntaxRewriter
......@@ -15,11 +15,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
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))
......
......@@ -11,7 +11,7 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithProperty
<ExportLanguageService(GetType(IReplacePropertyWithMethodsService), LanguageNames.VisualBasic), [Shared]>
Partial Friend Class VisualBasicReplacePropertyWithMethods
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, StatementSyntax)
Inherits AbstractReplacePropertyWithMethodsService(Of IdentifierNameSyntax, ExpressionSyntax, CrefReferenceSyntax, StatementSyntax)
Public Overrides Function GetPropertyDeclaration(token As SyntaxToken) As SyntaxNode
Dim containingProperty = token.Parent.FirstAncestorOrSelf(Of PropertyStatementSyntax)
......@@ -84,7 +84,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
result.Add(GetGetMethod(
generator, propertyStatement, propertyBackingField,
getMethod, desiredGetMethodName,
copyLeadingTrivia:=True,
cancellationToken:=cancellationToken))
End If
......@@ -93,7 +92,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
result.Add(GetSetMethod(
generator, propertyStatement, propertyBackingField,
setMethod, desiredSetMethodName,
copyLeadingTrivia:=getMethod Is Nothing,
cancellationToken:=cancellationToken))
End If
......@@ -106,7 +104,6 @@ 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)()
......@@ -123,7 +120,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
End If
Dim methodDeclaration = generator.MethodDeclaration(getMethod, desiredGetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToReturnsRewriter.instance)
Return methodDeclaration
End Function
......@@ -137,7 +134,6 @@ 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)()
......@@ -155,30 +151,34 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
End If
Dim methodDeclaration = generator.MethodDeclaration(setMethod, desiredSetMethodName, statements)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, copyLeadingTrivia)
methodDeclaration = CopyLeadingTriviaOver(propertyStatement, methodDeclaration, ConvertValueToParamRewriter.instance)
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
documentationCommentRewriter As VisualBasicSyntaxRewriter) As SyntaxNode
Return methodDeclaration.WithLeadingTrivia(
propertyStatement.GetLeadingTrivia().Select(Function(trivia) ConvertTrivia(trivia, documentationCommentRewriter)))
End Function
Private Function ConvertTrivia(trivia As SyntaxTrivia) As SyntaxTrivia
Private Function ConvertTrivia(trivia As SyntaxTrivia, documentationCommentRewriter As VisualBasicSyntaxRewriter) As SyntaxTrivia
If trivia.Kind() = SyntaxKind.DocumentationCommentTrivia Then
Dim converted = ConvertValueToReturnsRewriter.instance.Visit(trivia.GetStructure())
Dim converted = documentationCommentRewriter.Visit(trivia.GetStructure())
Return SyntaxFactory.Trivia(DirectCast(converted, StructuredTriviaSyntax))
End If
Return trivia
End Function
''' <summary>
''' Used by the documentation comment rewriters to identify top-level <c>&lt;value&gt;</c> nodes.
''' </summary>
Private Shared Function IsValueName(node As XmlNodeSyntax) As Boolean
Dim name = TryCast(node, XmlNameSyntax)
Return name?.Prefix Is Nothing AndAlso name?.LocalName.ValueText = "value"
End Function
Public Overrides Function GetPropertyNodeToReplace(propertyDeclaration As SyntaxNode) As SyntaxNode
' In VB we'll have the property statement. If that is parented by a
' property block, we'll want to replace that instead. Otherwise we
......@@ -188,6 +188,38 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.ReplaceMethodWithP
propertyDeclaration)
End Function
Protected Overrides Function TryGetCrefSyntax(identifierName As IdentifierNameSyntax) As CrefReferenceSyntax
Dim simpleNameCref = TryCast(identifierName.Parent, CrefReferenceSyntax)
If simpleNameCref IsNot Nothing Then
Return simpleNameCref
End If
Dim qualifiedName = TryCast(identifierName.Parent, QualifiedNameSyntax)
If qualifiedName Is Nothing Then
Return Nothing
End If
Return TryCast(qualifiedName.Parent, CrefReferenceSyntax)
End Function
Protected Overrides Function CreateCrefSyntax(originalCref As CrefReferenceSyntax, identifierToken As SyntaxToken, parameterType As SyntaxNode) As CrefReferenceSyntax
Dim signature As CrefSignatureSyntax
Dim parameterSyntax = TryCast(parameterType, TypeSyntax)
If parameterSyntax IsNot Nothing Then
signature = SyntaxFactory.CrefSignature(SyntaxFactory.CrefSignaturePart(modifier:=Nothing, type:=parameterSyntax))
Else
signature = SyntaxFactory.CrefSignature()
End If
Dim typeReference As TypeSyntax = SyntaxFactory.IdentifierName(identifierToken)
Dim qualifiedType = TryCast(originalCref.Name, QualifiedNameSyntax)
If qualifiedType IsNot Nothing Then
typeReference = qualifiedType.ReplaceNode(qualifiedType.GetLastDottedName(), typeReference)
End If
Return SyntaxFactory.CrefReference(typeReference, signature, asClause:=Nothing)
End Function
Protected Overrides Function UnwrapCompoundAssignment(compoundAssignment As SyntaxNode, readExpression As ExpressionSyntax) As ExpressionSyntax
Throw New InvalidOperationException("Compound assignments don't exist in VB")
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册