未验证 提交 3b3e771a 编写于 作者: M msftbot[bot] 提交者: GitHub

Merge pull request #47732 from allisonchou/GenerateConstructorNaming

Fix for generate constructor naming bug
...@@ -2527,7 +2527,7 @@ class C ...@@ -2527,7 +2527,7 @@ class C
{ {
void M(IEnumerable<int> nums) void M(IEnumerable<int> nums)
{ {
IEnumerable<int> queryables() IEnumerable<int> queryable()
{ {
foreach (int n1 in nums.AsQueryable()) foreach (int n1 in nums.AsQueryable())
{ {
...@@ -2535,7 +2535,7 @@ IEnumerable<int> queryables() ...@@ -2535,7 +2535,7 @@ IEnumerable<int> queryables()
} }
} }
IEnumerable<int> q = queryables(); IEnumerable<int> q = queryable();
} }
}"; }";
......
...@@ -4215,6 +4215,162 @@ public A(int* a, int b) : this(a) ...@@ -4215,6 +4215,162 @@ public A(int* a, int b) : this(a)
} }
public A(int* a, int b, int c) : this(a, b) { } public A(int* a, int b, int c) : this(a, b) { }
}");
}
[WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestGenerateNameFromTypeArgument()
{
await TestInRegularAndScriptAsync(
@"using System.Collections.Generic;
class Frog { }
class C
{
C M() => new [||]C(new List<Frog>());
}",
@"using System.Collections.Generic;
class Frog { }
class C
{
private List<Frog> frogs;
public C(List<Frog> frogs)
{
this.frogs = frogs;
}
C M() => new C(new List<Frog>());
}");
}
[WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestDoNotGenerateNameFromTypeArgumentIfNotEnumerable()
{
await TestInRegularAndScriptAsync(
@"class Frog<T> { }
class C
{
C M()
{
return new [||]C(new Frog<int>());
}
}",
@"class Frog<T> { }
class C
{
private Frog<int> frog;
public C(Frog<int> frog)
{
this.frog = frog;
}
C M()
{
return new C(new Frog<int>());
}
}");
}
[WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestGenerateNameFromTypeArgumentForErrorType()
{
await TestInRegularAndScriptAsync(
@"using System.Collections.Generic;
class Frog { }
class C
{
C M() => new [||]C(new List<>());
}",
@"using System.Collections.Generic;
class Frog { }
class C
{
private List<T> ts;
public C(List<T> ts)
{
this.ts = ts;
}
C M() => new C(new List<>());
}");
}
[WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestGenerateNameFromTypeArgumentForTupleType()
{
await TestInRegularAndScriptAsync(
@"using System.Collections.Generic;
class Frog { }
class C
{
C M() => new [||]C(new List<(int, string)>());
}",
@"using System.Collections.Generic;
class Frog { }
class C
{
private List<(int, string)> list;
public C(List<(int, string)> list)
{
this.list = list;
}
C M() => new C(new List<(int, string)>());
}");
}
[WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestGenerateNameFromTypeArgumentInNamespace()
{
await TestInRegularAndScriptAsync(
@"using System.Collections.Generic;
namespace N {
class Frog { }
class C
{
C M() => new [||]C(new List<Frog>());
}
}",
@"using System.Collections.Generic;
namespace N {
class Frog { }
class C
{
private List<Frog> frogs;
public C(List<Frog> frogs)
{
this.frogs = frogs;
}
C M() => new C(new List<Frog>());
}
}"); }");
} }
} }
......
...@@ -1634,8 +1634,8 @@ End Class") ...@@ -1634,8 +1634,8 @@ End Class")
End Module", End Module",
"Module Program "Module Program
Sub Main() Sub Main()
Dim {|Rename:v|} As Integer() = New Integer() {} Dim {|Rename:vs|} As Integer() = New Integer() {}
Return v Return vs
End Sub End Sub
End Module") End Module")
End Function End Function
...@@ -1953,8 +1953,8 @@ End Class", ...@@ -1953,8 +1953,8 @@ End Class",
Class C Class C
Shared Sub Main() Shared Sub Main()
Dim {|Rename:v|} As Integer() = New C().Goo() Dim {|Rename:vs|} As Integer() = New C().Goo()
Dim x = v(0) Dim x = vs(0)
End Sub End Sub
Function Goo() As Integer() Function Goo() As Integer()
End Function End Function
......
...@@ -2635,7 +2635,7 @@ Class M1 ...@@ -2635,7 +2635,7 @@ Class M1
sub1(Of Integer, String)(New Integer() {1, 2, 3}, New String() {"a", "b"}) sub1(Of Integer, String)(New Integer() {1, 2, 3}, New String() {"a", "b"})
End Sub End Sub
Private Sub sub1(Of T1, T2)(v1() As T1, v2() As T2) Private Sub sub1(Of T1, T2)(vs1() As T1, vs2() As T2)
Throw New NotImplementedException() Throw New NotImplementedException()
End Sub End Sub
End Class End Class
......
...@@ -1434,21 +1434,22 @@ End Enum ...@@ -1434,21 +1434,22 @@ End Enum
Public Class MyAttribute Public Class MyAttribute
Inherits System.Attribute Inherits System.Attribute
Private v1 As Short() Private vs As Short()
Private a1 As A Private a1 As A
Private v2 As Boolean Private v1 As Boolean
Private v3 As Integer Private v2 As Integer
Private v4 As Char Private v3 As Char
Private v5 As Short Private v4 As Short
Private v6 As Integer Private v5 As Integer
Private v7 As Long Private v6 As Long
Private v8 As Double Private v7 As Double
Private v9 As Single Private v8 As Single
Private v10 As String Private v9 As String
Public Sub New(v1() As Short, a1 As A, v2 As Boolean, v3 As Integer, v4 As Char, v5 As Short, v6 As Integer, v7 As Long, v8 As Double, v9 As Single, v10 As String) Public Sub New(vs() As Short, a1 As A, v1 As Boolean, v2 As Integer, v3 As Char, v4 As Short, v5 As Integer, v6 As Long, v7 As Double, v8 As Single, v9 As String)
Me.v1 = v1 Me.vs = vs
Me.a1 = a1 Me.a1 = a1
Me.v1 = v1
Me.v2 = v2 Me.v2 = v2
Me.v3 = v3 Me.v3 = v3
Me.v4 = v4 Me.v4 = v4
...@@ -1457,7 +1458,6 @@ Public Class MyAttribute ...@@ -1457,7 +1458,6 @@ Public Class MyAttribute
Me.v7 = v7 Me.v7 = v7
Me.v8 = v8 Me.v8 = v8
Me.v9 = v9 Me.v9 = v9
Me.v10 = v10
End Sub End Sub
End Class End Class
<MyAttribute(New Short(1) {1, 2, 3}, A.A1, True, 1, ""Z""c, 5S, 1I, 5L, 6.0R, 2.1F, ""abc"")> <MyAttribute(New Short(1) {1, 2, 3}, A.A1, True, 1, ""Z""c, 5S, 1I, 5L, 6.0R, 2.1F, ""abc"")>
...@@ -1984,6 +1984,70 @@ Class Test ...@@ -1984,6 +1984,70 @@ Class Test
Me.v = v Me.v = v
End Sub End Sub
End Class End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)>
<WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")>
Public Async Function TestGenerateNameFromTypeArgument() As Task
Await TestInRegularAndScriptAsync(
"Imports System.Collections.Generic
Class Frog
End Class
Class C
Private Function M() As C
Return New C([||]New List(Of Frog)())
End Function
End Class
",
"Imports System.Collections.Generic
Class Frog
End Class
Class C
Private frogs As List(Of Frog)
Public Sub New(frogs As List(Of Frog))
Me.frogs = frogs
End Sub
Private Function M() As C
Return New C(New List(Of Frog)())
End Function
End Class
")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)>
<WorkItem(44708, "https://github.com/dotnet/roslyn/issues/44708")>
Public Async Function TestDoNotGenerateNameFromTypeArgumentIfNotEnumerable() As Task
Await TestInRegularAndScriptAsync(
"Class Frog(Of T)
End Class
Class C
Private Function M() As C
Return New C([||]New Frog(Of Integer)())
End Function
End Class
",
"Class Frog(Of T)
End Class
Class C
Private frog As Frog(Of Integer)
Public Sub New(frog As Frog(Of Integer))
Me.frog = frog
End Sub
Private Function M() As C
Return New C(New Frog(Of Integer)())
End Function
End Class
") ")
End Function End Function
End Class End Class
......
...@@ -14,11 +14,14 @@ ...@@ -14,11 +14,14 @@
using Humanizer; using Humanizer;
using Roslyn.Utilities; using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
namespace Microsoft.CodeAnalysis.CSharp.Extensions namespace Microsoft.CodeAnalysis.CSharp.Extensions
{ {
internal static partial class SemanticModelExtensions internal static partial class SemanticModelExtensions
{ {
private const string DefaultParameterName = "p";
public static ImmutableArray<ParameterName> GenerateParameterNames( public static ImmutableArray<ParameterName> GenerateParameterNames(
this SemanticModel semanticModel, this SemanticModel semanticModel,
ArgumentListSyntax argumentList, ArgumentListSyntax argumentList,
...@@ -219,25 +222,12 @@ private static ImmutableArray<ParameterName> GenerateNames(IList<string> reserve ...@@ -219,25 +222,12 @@ private static ImmutableArray<ParameterName> GenerateNames(IList<string> reserve
// Otherwise, figure out the type of the expression and generate a name from that // Otherwise, figure out the type of the expression and generate a name from that
// instead. // instead.
var info = semanticModel.GetTypeInfo(expression, cancellationToken); var info = semanticModel.GetTypeInfo(expression, cancellationToken);
if (info.Type == null)
{
return DefaultParameterName;
}
// If we can't determine the type, then fallback to some placeholders. return semanticModel.GenerateNameFromType(info.Type, CSharpSyntaxFacts.Instance, capitalize);
var type = info.Type;
var pluralize = Pluralize(semanticModel, type);
var parameterName = type.CreateParameterName(capitalize);
return pluralize ? parameterName.Pluralize() : parameterName;
}
private static bool Pluralize(SemanticModel semanticModel, ITypeSymbol type)
{
if (type == null)
return false;
if (type.SpecialType == SpecialType.System_String)
return false;
var enumerableType = semanticModel.Compilation.IEnumerableOfTType();
return type.AllInterfaces.Any(i => i.OriginalDefinition.Equals(enumerableType));
} }
private static string TryGenerateNameForArgumentExpression( private static string TryGenerateNameForArgumentExpression(
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.BuildManager.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)" /> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.BuildManager.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Orchestrator" Partner="UnitTesting" Key="$(UnitTestingKey)" /> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Orchestrator" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Orchestrator.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)" /> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Orchestrator.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Test.Utilities" Partner="UnitTesting" Key="$(UnitTestingKey)"/> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.LiveUnitTesting.Test.Utilities" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServer.Protocol" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServer.Protocol" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServer.Protocol.UnitTests" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.LanguageServerIndexFormat.Generator" />
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.ServiceHub" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.ServiceHub" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Workspaces" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.Remote.Workspaces" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.UnitTesting.SourceBasedTestDiscovery" Partner="UnitTesting" Key="$(UnitTestingKey)" /> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.UnitTesting.SourceBasedTestDiscovery" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.UnitTesting.SourceBasedTestDiscovery.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)"/> <RestrictedInternalsVisibleTo Include="Microsoft.CodeAnalysis.UnitTesting.SourceBasedTestDiscovery.UnitTests" Partner="UnitTesting" Key="$(UnitTestingKey)" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.EditorFeatures" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.EditorFeatures" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.Features" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.Features" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" /> <InternalsVisibleTo Include="Microsoft.CodeAnalysis.VisualBasic.Workspaces" />
...@@ -136,6 +136,9 @@ ...@@ -136,6 +136,9 @@
<ItemGroup> <ItemGroup>
<EmbeddedResource Update="WorkspacesResources.resx" GenerateSource="true" /> <EmbeddedResource Update="WorkspacesResources.resx" GenerateSource="true" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="$(HumanizerCoreVersion)" PrivateAssets="compile" />
</ItemGroup>
<ItemGroup> <ItemGroup>
<PublicAPI Include="PublicAPI.Shipped.txt" /> <PublicAPI Include="PublicAPI.Shipped.txt" />
<PublicAPI Include="PublicAPI.Unshipped.txt" /> <PublicAPI Include="PublicAPI.Unshipped.txt" />
......
...@@ -6,10 +6,13 @@ ...@@ -6,10 +6,13 @@
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Humanizer;
using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Shared.Extensions namespace Microsoft.CodeAnalysis.Shared.Extensions
{ {
...@@ -171,5 +174,64 @@ public static SemanticMap GetSemanticMap(this SemanticModel semanticModel, Synta ...@@ -171,5 +174,64 @@ public static SemanticMap GetSemanticMap(this SemanticModel semanticModel, Synta
return new TokenSemanticInfo(declaredSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span); return new TokenSemanticInfo(declaredSymbol, aliasSymbol, allSymbols, type, convertedType, token.Span);
} }
public static string GenerateNameFromType(this SemanticModel semanticModel, ITypeSymbol type, ISyntaxFacts syntaxFacts, bool capitalize)
{
var pluralize = semanticModel.ShouldPluralize(type);
var typeArguments = type.GetAllTypeArguments();
// We may be able to use the type's arguments to generate a name if we're working with an enumerable type.
if (pluralize && TryGeneratePluralizedNameFromTypeArgument(syntaxFacts, typeArguments, capitalize, out var typeArgumentParameterName))
{
return typeArgumentParameterName;
}
// If there's no type argument and we have an array type, we should pluralize, e.g. using 'frogs' for 'new Frog[]' instead of 'frog'
if (type.TypeKind == TypeKind.Array && typeArguments.IsEmpty)
{
return type.CreateParameterName(capitalize).Pluralize();
}
// Otherwise assume no pluralization, e.g. using 'immutableArray', 'list', etc. instead of their
// plural forms
return type.CreateParameterName(capitalize);
}
private static bool ShouldPluralize(this SemanticModel semanticModel, ITypeSymbol type)
{
if (type == null)
return false;
// string implements IEnumerable<char>, so we need to specifically exclude it.
if (type.SpecialType == SpecialType.System_String)
return false;
var enumerableType = semanticModel.Compilation.IEnumerableOfTType();
return type.AllInterfaces.Any(i => i.OriginalDefinition.Equals(enumerableType));
}
private static bool TryGeneratePluralizedNameFromTypeArgument(
ISyntaxFacts syntaxFacts,
ImmutableArray<ITypeSymbol> typeArguments,
bool capitalize,
[NotNullWhen(true)] out string? parameterName)
{
// We only consider generating a name if there's one type argument.
// This logic can potentially be expanded upon in the future.
if (typeArguments.Length == 1)
{
// We only want the last part of the type, i.e. we don't want namespaces.
var typeArgument = typeArguments.Single().ToDisplayParts().Last().ToString();
if (syntaxFacts.IsValidIdentifier(typeArgument))
{
typeArgument = typeArgument.Pluralize();
parameterName = capitalize ? typeArgument.ToPascalCase() : typeArgument.ToCamelCase();
return true;
}
}
parameterName = null;
return false;
}
} }
} }
...@@ -10,9 +10,6 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax ...@@ -10,9 +10,6 @@ Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Partial Friend Module SemanticModelExtensions Partial Friend Module SemanticModelExtensions
Private Const s_defaultParameterName = "p"
<Extension()> <Extension()>
Public Function LookupTypeRegardlessOfArity(semanticModel As SemanticModel, Public Function LookupTypeRegardlessOfArity(semanticModel As SemanticModel,
name As SyntaxToken, name As SyntaxToken,
...@@ -78,97 +75,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions ...@@ -78,97 +75,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return semanticModel.GetSymbolInfo(expression) Return semanticModel.GetSymbolInfo(expression)
End Function End Function
<Extension()>
Public Function GenerateNameForArgument(semanticModel As SemanticModel,
argument As ArgumentSyntax,
cancellationToken As CancellationToken) As String
Dim result = GenerateNameForArgumentWorker(semanticModel, argument, cancellationToken)
Return If(String.IsNullOrWhiteSpace(result), s_defaultParameterName, result)
End Function
Private Function GenerateNameForArgumentWorker(semanticModel As SemanticModel,
argument As ArgumentSyntax,
cancellationToken As CancellationToken) As String
If argument.IsNamed Then
Return DirectCast(argument, SimpleArgumentSyntax).NameColonEquals.Name.Identifier.ValueText
ElseIf Not argument.IsOmitted Then
Return semanticModel.GenerateNameForExpression(
argument.GetExpression(), capitalize:=False, cancellationToken:=cancellationToken)
Else
Return s_defaultParameterName
End If
End Function
''' <summary>
''' Given an expression node, tries to generate an appropriate name that can be used for
''' that expression.
''' </summary>
<Extension()>
Public Function GenerateNameForExpression(semanticModel As SemanticModel,
expression As ExpressionSyntax,
capitalize As Boolean,
cancellationToken As CancellationToken) As String
' Try to find a usable name node that we can use to name the
' parameter. If we have an expression that has a name as part of it
' then we try to use that part.
Dim current = expression
While True
current = current.WalkDownParentheses()
If current.Kind = SyntaxKind.IdentifierName Then
Return (DirectCast(current, IdentifierNameSyntax)).Identifier.ValueText.ToCamelCase()
ElseIf TypeOf current Is MemberAccessExpressionSyntax Then
Return (DirectCast(current, MemberAccessExpressionSyntax)).Name.Identifier.ValueText.ToCamelCase()
ElseIf TypeOf current Is CastExpressionSyntax Then
current = (DirectCast(current, CastExpressionSyntax)).Expression
Else
Exit While
End If
End While
' there was nothing in the expression to signify a name. If we're in an argument
' location, then try to choose a name based on the argument name.
Dim argumentName = TryGenerateNameForArgumentExpression(
semanticModel, expression, cancellationToken)
If argumentName IsNot Nothing Then
Return If(capitalize, argumentName.ToPascalCase(), argumentName.ToCamelCase())
End If
' Otherwise, figure out the type of the expression and generate a name from that
' instead.
Dim info = semanticModel.GetTypeInfo(expression, cancellationToken)
' If we can't determine the type, then fallback to some placeholders.
Dim [type] = info.Type
Return [type].CreateParameterName(capitalize)
End Function
Private Function TryGenerateNameForArgumentExpression(semanticModel As SemanticModel, expression As ExpressionSyntax, cancellationToken As CancellationToken) As String
Dim topExpression = expression.WalkUpParentheses()
If TypeOf topExpression.Parent Is ArgumentSyntax Then
Dim argument = DirectCast(topExpression.Parent, ArgumentSyntax)
Dim simpleArgument = TryCast(argument, SimpleArgumentSyntax)
If simpleArgument?.NameColonEquals IsNot Nothing Then
Return simpleArgument.NameColonEquals.Name.Identifier.ValueText
End If
Dim argumentList = TryCast(argument.Parent, ArgumentListSyntax)
If argumentList IsNot Nothing Then
Dim index = argumentList.Arguments.IndexOf(argument)
Dim member = TryCast(semanticModel.GetSymbolInfo(argumentList.Parent, cancellationToken).Symbol, IMethodSymbol)
If member IsNot Nothing AndAlso index < member.Parameters.Length Then
Dim parameter = member.Parameters(index)
If parameter.Type.TypeKind <> TypeKind.TypeParameter Then
Return parameter.Name
End If
End If
End If
End If
Return Nothing
End Function
<Extension()> <Extension()>
Public Function GetImportNamespacesInScope(semanticModel As SemanticModel, location As SyntaxNode) As ISet(Of INamespaceSymbol) Public Function GetImportNamespacesInScope(semanticModel As SemanticModel, location As SyntaxNode) As ISet(Of INamespaceSymbol)
Dim q = Dim q =
......
...@@ -9,10 +9,13 @@ Imports Microsoft.CodeAnalysis ...@@ -9,10 +9,13 @@ Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles Imports Microsoft.CodeAnalysis.Diagnostics.Analyzers.NamingStyles
Imports Microsoft.CodeAnalysis.Utilities Imports Microsoft.CodeAnalysis.Utilities
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Partial Friend Module SemanticModelExtensions Partial Friend Module SemanticModelExtensions
Private Const DefaultParameterName = "p"
<Extension()> <Extension()>
Public Function GenerateParameterNames(semanticModel As SemanticModel, Public Function GenerateParameterNames(semanticModel As SemanticModel,
arguments As ArgumentListSyntax, arguments As ArgumentListSyntax,
...@@ -95,5 +98,97 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions ...@@ -95,5 +98,97 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Select(Function(name, index) New ParameterName(name, isFixed(index), parameterNamingRule)). Select(Function(name, index) New ParameterName(name, isFixed(index), parameterNamingRule)).
ToImmutableArray() ToImmutableArray()
End Function End Function
<Extension()>
Public Function GenerateNameForArgument(semanticModel As SemanticModel,
argument As ArgumentSyntax,
cancellationToken As CancellationToken) As String
Dim result = GenerateNameForArgumentWorker(semanticModel, argument, cancellationToken)
Return If(String.IsNullOrWhiteSpace(result), DefaultParameterName, result)
End Function
Private Function GenerateNameForArgumentWorker(semanticModel As SemanticModel,
argument As ArgumentSyntax,
cancellationToken As CancellationToken) As String
If argument.IsNamed Then
Return DirectCast(argument, SimpleArgumentSyntax).NameColonEquals.Name.Identifier.ValueText
ElseIf Not argument.IsOmitted Then
Return semanticModel.GenerateNameForExpression(
argument.GetExpression(), capitalize:=False, cancellationToken:=cancellationToken)
Else
Return DefaultParameterName
End If
End Function
''' <summary>
''' Given an expression node, tries to generate an appropriate name that can be used for
''' that expression.
''' </summary>
<Extension()>
Public Function GenerateNameForExpression(semanticModel As SemanticModel,
expression As ExpressionSyntax,
capitalize As Boolean,
cancellationToken As CancellationToken) As String
' Try to find a usable name node that we can use to name the
' parameter. If we have an expression that has a name as part of it
' then we try to use that part.
Dim current = expression
While True
current = current.WalkDownParentheses()
If current.Kind = SyntaxKind.IdentifierName Then
Return (DirectCast(current, IdentifierNameSyntax)).Identifier.ValueText.ToCamelCase()
ElseIf TypeOf current Is MemberAccessExpressionSyntax Then
Return (DirectCast(current, MemberAccessExpressionSyntax)).Name.Identifier.ValueText.ToCamelCase()
ElseIf TypeOf current Is CastExpressionSyntax Then
current = (DirectCast(current, CastExpressionSyntax)).Expression
Else
Exit While
End If
End While
' there was nothing in the expression to signify a name. If we're in an argument
' location, then try to choose a name based on the argument name.
Dim argumentName = TryGenerateNameForArgumentExpression(
semanticModel, expression, cancellationToken)
If argumentName IsNot Nothing Then
Return If(capitalize, argumentName.ToPascalCase(), argumentName.ToCamelCase())
End If
' Otherwise, figure out the type of the expression and generate a name from that
' instead.
Dim info = semanticModel.GetTypeInfo(expression, cancellationToken)
If info.Type Is Nothing Then
Return DefaultParameterName
End If
Return semanticModel.GenerateNameFromType(info.Type, VisualBasicSyntaxFacts.Instance, capitalize)
End Function
Private Function TryGenerateNameForArgumentExpression(semanticModel As SemanticModel, expression As ExpressionSyntax, cancellationToken As CancellationToken) As String
Dim topExpression = expression.WalkUpParentheses()
If TypeOf topExpression.Parent Is ArgumentSyntax Then
Dim argument = DirectCast(topExpression.Parent, ArgumentSyntax)
Dim simpleArgument = TryCast(argument, SimpleArgumentSyntax)
If simpleArgument?.NameColonEquals IsNot Nothing Then
Return simpleArgument.NameColonEquals.Name.Identifier.ValueText
End If
Dim argumentList = TryCast(argument.Parent, ArgumentListSyntax)
If argumentList IsNot Nothing Then
Dim index = argumentList.Arguments.IndexOf(argument)
Dim member = TryCast(semanticModel.GetSymbolInfo(argumentList.Parent, cancellationToken).Symbol, IMethodSymbol)
If member IsNot Nothing AndAlso index < member.Parameters.Length Then
Dim parameter = member.Parameters(index)
If parameter.Type.TypeKind <> TypeKind.TypeParameter Then
Return parameter.Name
End If
End If
End If
End If
Return Nothing
End Function
End Module End Module
End Namespace End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册