提交 9add1fa8 编写于 作者: G Gen Lu

Handle using alias in source

上级 e636d3df
......@@ -201,6 +201,196 @@ public void M(int x)
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInDeclaration_PrimitiveType(ReferenceType refType)
{
var file1 = $@"
using System;
using MyInt = System.Int32;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod(this MyInt x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(int x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInDeclaration_RegularType(ReferenceType refType)
{
var file1 = $@"
using System;
using MyAlias = System.Exception;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod(this MyAlias x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(Exception x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInDeclaration_GenericType(ReferenceType refType)
{
var file1 = $@"
using System;
using MyAlias = System.Collections.Generic.List<int>;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod(this MyAlias x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(System.Collections.Generic.List<int> x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInDeclaration_RegularTypeWithSameSimpleName(ReferenceType refType)
{
var file1 = $@"
using DataTime = System.Exception;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod(this System.DateTime x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(DateTime x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInDeclaration_Namespace(ReferenceType refType)
{
var file1 = $@"
using System;
using GenericCollection = System.Collections.Generic;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod<T>(this GenericCollection.List<T> x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(System.Collections.Generic.List<int> x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
displayTextSuffix: "<>",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[MemberData(nameof(ReferenceTypeData))]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task UsingAliasInUsage(ReferenceType refType)
......@@ -533,6 +723,53 @@ public void M(int x)
inlineDescription: "Foo");
}
[InlineData(ReferenceType.Project)]
[InlineData(ReferenceType.Metadata)]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestConflictingInternalExtensionMethods_NoIVT_InReference(ReferenceType refType)
{
var file1 = $@"
using System;
namespace Foo
{{
internal static class ExtensionClass
{{
public static bool ExtentionMethod(this int x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Foo
{{
internal static class ExtensionClass
{{
public static bool ExtentionMethod(this int x)
=> true;
}}
}}
namespace Baz
{{
public class Bat
{{
public void M(int x)
{{
x.$$
}}
}}
}}";
var markup = GetMarkup(file2, file1, refType);
await VerifyTypeImportItemExistsAsync(
markup,
"ExtentionMethod",
glyph: (int)Glyph.ExtensionMethodPublic,
inlineDescription: "Foo");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestInternalExtensionMethods_NoIVT_InSameProject()
{
......@@ -569,9 +806,9 @@ public void M(int x)
inlineDescription: "Foo");
}
// SymbolTreeInfo explicitly ignores non-public types from metadata(likely for perf reasons). So we don't need to test internals in PE reference
[InlineData(ReferenceType.None)]
[InlineData(ReferenceType.Project)]
[InlineData(ReferenceType.Metadata, Skip = "SymbolTreeInfo explicitly ignores non-public types from metadata(likely for perf reasons). Need to decide if this is the design we'd like.")]
[Theory, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task TestInternalExtensionMethods_WithIVT(ReferenceType refType)
{
......@@ -821,42 +1058,6 @@ public void M(int x)
inlineDescription: "");
}
[Fact, Trait(Traits.Feature, Traits.Features.Completion)]
public async Task ShouldNotProvideItemsIfIndexIsNotReady()
{
var file1 = $@"
using System;
namespace Foo
{{
public static class ExtensionClass
{{
public static bool ExtentionMethod(this int x)
=> true;
}}
}}";
var file2 = $@"
using System;
namespace Baz
{{
public class Bat
{{
public void M(int x)
{{
x.$$
}}
}}
}}";
IsExpandedCompletion = false;
var markup = GetMarkup(file2, file1, ReferenceType.Project);
await VerifyTypeImportItemIsAbsentAsync(
markup,
"ExtentionMethod",
inlineDescription: "Foo");
}
private Task VerifyTypeImportItemExistsAsync(string markup, string expectedItem, int glyph, string inlineDescription, string displayTextSuffix = null, string expectedDescriptionOrNull = null)
{
return VerifyItemExistsAsync(markup, expectedItem, displayTextSuffix: displayTextSuffix, glyph: glyph, inlineDescription: inlineDescription, expectedDescriptionOrNull: expectedDescriptionOrNull);
......
......@@ -24,15 +24,14 @@ internal enum ActionInfo
TargetTypeCompletionTicks,
ExtensionMethodCompletionSuccessCount,
// following are only reported when sucessful (i.e. filter is available)
ExtensionMethodCompletionTicks,
ExtensionMethodCompletionMethodsProvided,
ExtensionMethodCompletionGetFilterTicks, // only reported when filter is available
ExtensionMethodCompletionGetSymbolWithFilterTicks,
ExtensionMethodCompletionGetSymbolNoFilterTicks,
ExtensionMethodCompletionTypesCheckedWithFilter,
ExtensionMethodCompletionTypesCheckedNoFilter,
ExtensionMethodCompletionMethodsCheckedWithFilter,
ExtensionMethodCompletionMethodsCheckedNoFilter
ExtensionMethodCompletionGetFilterTicks,
ExtensionMethodCompletionGetSymbolTicks,
ExtensionMethodCompletionTypesChecked,
ExtensionMethodCompletionMethodsChecked,
}
internal static void LogTypeImportCompletionTicksDataPoint(int count) =>
......@@ -51,27 +50,26 @@ internal enum ActionInfo
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.TargetTypeCompletionTicks, count);
internal static void LogExtensionMethodCompletionSuccess() =>
s_logAggregator.IncreaseCount((int)ActionInfo.ExtensionMethodCompletionSuccessCount);
internal static void LogExtensionMethodCompletionTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionTicks, count);
internal static void LogExtensionMethodCompletionMethodsProvidedDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionMethodsProvided, count);
internal static void LogExtensionMethodCompletionGetFilterTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionGetFilterTicks, count);
internal static void LogExtensionMethodCompletionGetSymbolWithFilterTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionGetSymbolWithFilterTicks, count);
internal static void LogExtensionMethodCompletionGetSymbolNoFilterTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionGetSymbolNoFilterTicks, count);
internal static void LogExtensionMethodCompletionGetSymbolTicksDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionGetSymbolTicks, count);
internal static void LogExtensionMethodCompletionTypesCheckedWithFilterDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionTypesCheckedWithFilter, count);
internal static void LogExtensionMethodCompletionTypesCheckedNoFilterDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionTypesCheckedNoFilter, count);
internal static void LogExtensionMethodCompletionTypesCheckedDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionTypesChecked, count);
internal static void LogExtensionMethodCompletionMethodsCheckedWithFilterDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionMethodsCheckedWithFilter, count);
internal static void LogExtensionMethodCompletionMethodsCheckedNoFilterDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionMethodsCheckedNoFilter, count);
internal static void LogExtensionMethodCompletionMethodsCheckedDataPoint(int count) =>
s_statisticLogAggregator.AddDataPoint((int)ActionInfo.ExtensionMethodCompletionMethodsChecked, count);
internal static void ReportTelemetry()
......
......@@ -495,78 +495,98 @@ private bool IsExtensionMethod(MethodDeclarationSyntax method)
public override string GetRootNamespace(CompilationOptions compilationOptions)
=> string.Empty;
public override bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases)
{
if (node is UsingDirectiveSyntax usingDirectiveNode && usingDirectiveNode.Alias != null)
{
if (TryGetSimpleTypeName(usingDirectiveNode.Alias.Name, typeParameterNames: null, out var aliasName) &&
TryGetSimpleTypeName(usingDirectiveNode.Name, typeParameterNames: null, out var name))
{
aliases = ImmutableArray.Create<(string, string)>((aliasName, name));
return true;
}
}
aliases = default;
return false;
}
public override bool TryGetTargetTypeName(SyntaxNode node, out string targetTypeName)
{
if (node is MethodDeclarationSyntax methodDeclaration && IsExtensionMethod(methodDeclaration))
{
var typeParameterNames = methodDeclaration.TypeParameterList?.Parameters.SelectAsArray(p => p.Identifier.Text);
targetTypeName = GetTypeName(methodDeclaration.ParameterList.Parameters[0].Type, typeParameterNames);
TryGetSimpleTypeName(methodDeclaration.ParameterList.Parameters[0].Type, typeParameterNames, out targetTypeName);
// We always return true here, which indicates we have a valid taget type name. (which would be null for a complex type though).
return true;
}
targetTypeName = null;
return false;
}
static string GetTypeName(TypeSyntax typeNode, ImmutableArray<string>? typeParameterNames)
private static bool TryGetSimpleTypeName(SyntaxNode node, ImmutableArray<string>? typeParameterNames, out string simpleTypeName)
{
if (node is TypeSyntax typeNode)
{
switch (typeNode)
{
case IdentifierNameSyntax identifierNameNode:
// We consider it a complext method if the receiver type is a type parameter.
// We consider it a complex method if the receiver type is a type parameter.
var text = identifierNameNode.Identifier.Text;
return typeParameterNames?.Contains(text) == true
? null
: text;
simpleTypeName = typeParameterNames?.Contains(text) == true ? null : text;
return simpleTypeName != null;
case GenericNameSyntax genericNameNode:
var name = genericNameNode.Identifier.Text;
var arity = genericNameNode.Arity;
return arity == 0
? name
: name + GetMetadataAritySuffix(arity);
simpleTypeName = arity == 0 ? name : name + GetMetadataAritySuffix(arity);
return true;
case PredefinedTypeSyntax predefinedTypeNode:
return GetSpecialTypeName(predefinedTypeNode);
simpleTypeName = GetSpecialTypeName(predefinedTypeNode);
return simpleTypeName != null;
case AliasQualifiedNameSyntax aliasQualifiedNameNode:
return GetTypeName(aliasQualifiedNameNode.Name, typeParameterNames);
return TryGetSimpleTypeName(aliasQualifiedNameNode.Name, typeParameterNames, out simpleTypeName);
case QualifiedNameSyntax qualifiedNameNode:
// For an identifier to the right of a '.', it can't be a type parameter,
// so we don't need to check for it further.
return GetTypeName(qualifiedNameNode.Right, null);
return TryGetSimpleTypeName(qualifiedNameNode.Right, typeParameterNames: null, out simpleTypeName);
case NullableTypeSyntax nullableNode:
return GetTypeName(nullableNode.ElementType, typeParameterNames);
return TryGetSimpleTypeName(nullableNode.ElementType, typeParameterNames, out simpleTypeName);
}
// This is a complex type, we will not try to filter them with type name.
return null;
}
static string GetSpecialTypeName(PredefinedTypeSyntax predefinedTypeNode)
simpleTypeName = null;
return false;
}
private static string GetSpecialTypeName(PredefinedTypeSyntax predefinedTypeNode)
{
var kind = predefinedTypeNode.Keyword.Kind();
return kind switch
{
var kind = predefinedTypeNode.Keyword.Kind();
return kind switch
{
SyntaxKind.BoolKeyword => "Boolean",
SyntaxKind.ByteKeyword => "Byte",
SyntaxKind.SByteKeyword => "SByte",
SyntaxKind.ShortKeyword => "Int16",
SyntaxKind.UShortKeyword => "UInt16",
SyntaxKind.IntKeyword => "Int32",
SyntaxKind.UIntKeyword => "UInt32",
SyntaxKind.LongKeyword => "Int64",
SyntaxKind.ULongKeyword => "UInt64",
SyntaxKind.DoubleKeyword => "Double",
SyntaxKind.FloatKeyword => "Single",
SyntaxKind.DecimalKeyword => "Decimal",
SyntaxKind.StringKeyword => "String",
SyntaxKind.CharKeyword => "Char",
SyntaxKind.ObjectKeyword => "Object",
_ => null,
};
}
SyntaxKind.BoolKeyword => "Boolean",
SyntaxKind.ByteKeyword => "Byte",
SyntaxKind.SByteKeyword => "SByte",
SyntaxKind.ShortKeyword => "Int16",
SyntaxKind.UShortKeyword => "UInt16",
SyntaxKind.IntKeyword => "Int32",
SyntaxKind.UIntKeyword => "UInt32",
SyntaxKind.LongKeyword => "Int64",
SyntaxKind.ULongKeyword => "UInt64",
SyntaxKind.DoubleKeyword => "Double",
SyntaxKind.FloatKeyword => "Single",
SyntaxKind.DecimalKeyword => "Decimal",
SyntaxKind.StringKeyword => "String",
SyntaxKind.CharKeyword => "Char",
SyntaxKind.ObjectKeyword => "Object",
_ => null,
};
}
}
}
......@@ -200,6 +200,9 @@ public bool IsUsingDirectiveName(SyntaxNode node)
((UsingDirectiveSyntax)node.Parent).Name == node;
}
public bool IsUsingAliasDirective(SyntaxNode node)
=> node is UsingDirectiveSyntax usingDirectiveNode && usingDirectiveNode.Alias != null;
public bool IsForEachStatement(SyntaxNode node)
=> node is ForEachStatementSyntax;
......@@ -1936,8 +1939,5 @@ public void GetPartsOfCastExpression(SyntaxNode node, out SyntaxNode type, out S
public override SyntaxList<SyntaxNode> GetAttributeLists(SyntaxNode node)
=> CSharpSyntaxGenerator.GetAttributeLists(node);
public bool IsUsingAliasDirective(SyntaxNode node)
=> node is UsingDirectiveSyntax usingDirectiveNode && usingDirectiveNode.Alias != null;
}
}
......@@ -425,17 +425,18 @@ private void GenerateMetadataNodes()
method.GetParameters().Count > 0 &&
method.GetCustomAttributes().Count > 0)
{
_containsExtensionsMethod = true;
// Decode method signature to get the target type name (i.e. type name for the first parameter)
var blob = _metadataReader.GetBlobReader(method.Signature);
var decoder = new SignatureDecoder<ParameterTypeInfo, object>(ParameterTypeInfoProvider.Instance, _metadataReader, genericContext: null);
var signature = decoder.DecodeMethodSignature(ref blob);
var firstParameterTypeInfo = signature.ParameterTypes[0];
var definition = new MetadataDefinition(MetadataDefinitionKind.Member, _metadataReader.GetString(method.Name), firstParameterTypeInfo);
definitionMap.Add(definition.Name, definition);
if (signature.ParameterTypes.Length > 0)
{
_containsExtensionsMethod = true;
var firstParameterTypeInfo = signature.ParameterTypes[0];
var definition = new MetadataDefinition(MetadataDefinitionKind.Member, _metadataReader.GetString(method.Name), firstParameterTypeInfo);
definitionMap.Add(definition.Name, definition);
}
}
}
}
......
......@@ -23,6 +23,8 @@ internal interface IDeclaredSymbolInfoFactoryService : ILanguageService
bool TryGetTargetTypeName(SyntaxNode node, out string instanceTypeName);
bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases);
string GetRootNamespace(CompilationOptions compilationOptions);
}
......@@ -69,6 +71,7 @@ internal sealed partial class SyntaxTreeIndex
var complexExtensionMethodInfoBuilder = ArrayBuilder<int>.GetInstance();
var rootNamespace = infoFactory.GetRootNamespace(project.CompilationOptions);
var usingAliases = PooledDictionary<string, string>.GetInstance();
try
{
......@@ -83,7 +86,6 @@ internal sealed partial class SyntaxTreeIndex
var containsDeconstruction = false;
var containsAwait = false;
var containsTupleExpressionOrTupleType = false;
var containsUsingAliasDirective = false;
var predefinedTypes = (int)PredefinedType.None;
var predefinedOperators = (int)PredefinedOperator.None;
......@@ -114,7 +116,14 @@ internal sealed partial class SyntaxTreeIndex
containsTupleExpressionOrTupleType = containsTupleExpressionOrTupleType ||
syntaxFacts.IsTupleExpression(node) || syntaxFacts.IsTupleType(node);
containsUsingAliasDirective = containsUsingAliasDirective || syntaxFacts.IsUsingAliasDirective(node);
if (syntaxFacts.IsUsingAliasDirective(node) && infoFactory.TryGetAliasesFromUsingDirective(node, out var aliases))
{
foreach (var (aliasName, name) in aliases)
{
Debug.Assert(!usingAliases.ContainsKey(aliasName));
usingAliases[aliasName] = name;
}
}
// We've received a number of error reports where DeclaredSymbolInfo.GetSymbolAsync() will
// crash because the document's syntax root doesn't contain the span of the node returned
......@@ -136,7 +145,7 @@ internal sealed partial class SyntaxTreeIndex
AddExtensionMethodInfo(
infoFactory,
node,
containsUsingAliasDirective,
usingAliases,
declaredSymbolInfoIndex,
declaredSymbolInfo,
simpleExtensionMethodInfoBuilder,
......@@ -249,12 +258,13 @@ internal sealed partial class SyntaxTreeIndex
LongLiteralHashSetPool.ClearAndFree(longLiterals);
simpleExtensionMethodInfoBuilder.Free();
complexExtensionMethodInfoBuilder.Free();
usingAliases.Free();
}
static void AddExtensionMethodInfo(
IDeclaredSymbolInfoFactoryService infoFactory,
SyntaxNode node,
bool containsUsingAliasDirective,
PooledDictionary<string, string> aliases,
int declaredSymbolInfoIndex,
DeclaredSymbolInfo declaredSymbolInfo,
PooledDictionary<string, ArrayBuilder<int>> simpleInfoBuilder,
......@@ -265,14 +275,6 @@ internal sealed partial class SyntaxTreeIndex
return;
}
// If there's using alias, we don't even try to figure out if the alias is related
// to the target type, simply treated it as complex type.
if (containsUsingAliasDirective)
{
complexInfoBuilder.Add(declaredSymbolInfoIndex);
return;
}
if (!infoFactory.TryGetTargetTypeName(node, out var targetTypeName))
{
return;
......@@ -285,6 +287,11 @@ internal sealed partial class SyntaxTreeIndex
return;
}
if (aliases.TryGetValue(targetTypeName, out var originalName))
{
targetTypeName = originalName;
}
// simple type
if (!simpleInfoBuilder.TryGetValue(targetTypeName, out var arrayBuilder))
{
......
......@@ -100,6 +100,8 @@ public static string GetMetadataAritySuffix(int arity)
/// </summary>
public abstract bool TryGetTargetTypeName(SyntaxNode node, out string targetTypeName);
public abstract bool TryGetAliasesFromUsingDirective(SyntaxNode node, out ImmutableArray<(string aliasName, string name)> aliases);
public abstract string GetRootNamespace(CompilationOptions compilationOptions);
}
......
......@@ -252,6 +252,7 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsIdentifierName(SyntaxNode node);
bool IsGenericName(SyntaxNode node);
bool IsQualifiedName(SyntaxNode node);
bool IsUsingAliasDirective(SyntaxNode node);
bool IsAttribute(SyntaxNode node);
bool IsAttributeName(SyntaxNode node);
......@@ -454,8 +455,6 @@ internal interface ISyntaxFactsService : ILanguageService
SyntaxToken? GetDeclarationIdentifierIfOverride(SyntaxToken token);
bool SpansPreprocessorDirective(IEnumerable<SyntaxNode> nodes);
bool IsUsingAliasDirective(SyntaxNode node);
}
[Flags]
......
......@@ -2,6 +2,7 @@
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Runtime.CompilerServices
Imports System.Text
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.FindSymbols
......@@ -294,7 +295,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
Dim parameterCount = node.SubOrFunctionStatement.ParameterList?.Parameters.Count
' Extension method must have at least one parameter and declared inside a module
If Not parameterCount.HasValue Or parameterCount.Value = 0 Or TypeOf node.Parent IsNot ModuleBlockSyntax Then
If Not parameterCount.HasValue OrElse parameterCount.Value = 0 OrElse TypeOf node.Parent IsNot ModuleBlockSyntax Then
Return False
End If
......@@ -462,7 +463,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
If funcDecl IsNot Nothing And IsExtensionMethod(funcDecl) Then
Dim typeParameterNames = funcDecl.SubOrFunctionStatement.TypeParameterList?.Parameters.SelectAsArray(Function(p) p.Identifier.Text)
targetTypeName = GetTargetTypeName(funcDecl.BlockStatement.ParameterList.Parameters(0).AsClause?.Type, typeParameterNames)
TryGetSimpleTypeNameWorker(funcDecl.BlockStatement.ParameterList.Parameters(0).AsClause?.Type, typeParameterNames, targetTypeName)
Return True
End If
......@@ -470,36 +471,70 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.FindSymbols
Return False
End Function
Private Function GetTargetTypeName(typeSyntax As TypeSyntax, typeParameterNames As ImmutableArray(Of String)?) As String
Public Overrides Function TryGetAliasesFromUsingDirective(node As SyntaxNode, ByRef aliases As ImmutableArray(Of (aliasName As String, name As String))) As Boolean
If TypeOf typeSyntax Is IdentifierNameSyntax Then
Dim identifierName = DirectCast(typeSyntax, IdentifierNameSyntax)
Dim importStatement = TryCast(node, ImportsStatementSyntax)
Dim builder = ArrayBuilder(Of (String, String)).GetInstance()
If (importStatement IsNot Nothing) Then
For Each importsClause In importStatement.ImportsClauses
If importsClause.Kind = SyntaxKind.SimpleImportsClause Then
Dim simpleImportsClause = DirectCast(importsClause, SimpleImportsClauseSyntax)
Dim aliasName, name As String
#Disable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
If simpleImportsClause.Alias IsNot Nothing And
TryGetSimpleTypeNameWorker(simpleImportsClause.Alias, Nothing, aliasName) And
TryGetSimpleTypeNameWorker(simpleImportsClause, Nothing, name) Then
#Enable Warning BC42030 ' Variable is passed by reference before it has been assigned a value
builder.Add((aliasName, name))
End If
End If
Next
aliases = builder.ToImmutableAndFree()
Return True
End If
aliases = Nothing
Return False
End Function
Private Shared Function TryGetSimpleTypeNameWorker(node As SyntaxNode, typeParameterNames As ImmutableArray(Of String)?, ByRef simpleTypeName As String) As Boolean
If TypeOf node Is IdentifierNameSyntax Then
Dim identifierName = DirectCast(node, IdentifierNameSyntax)
Dim text = identifierName.Identifier.Text
Return If(typeParameterNames?.Contains(text), Nothing, text)
simpleTypeName = If(typeParameterNames?.Contains(text), Nothing, text)
Return simpleTypeName IsNot Nothing
ElseIf TypeOf typeSyntax Is GenericNameSyntax Then
Dim genericName = DirectCast(typeSyntax, GenericNameSyntax)
ElseIf TypeOf node Is GenericNameSyntax Then
Dim genericName = DirectCast(node, GenericNameSyntax)
Dim name = genericName.Identifier.Text
Dim arity = genericName.Arity
Return If(arity = 0, name, name + GetMetadataAritySuffix(arity))
simpleTypeName = If(arity = 0, name, name + GetMetadataAritySuffix(arity))
Return True
ElseIf TypeOf typeSyntax Is QualifiedNameSyntax Then
ElseIf TypeOf node Is QualifiedNameSyntax Then
' For an identifier to the right of a '.', it can't be a type parameter,
' so we don't need to check for it further.
Dim qualifiedName = DirectCast(typeSyntax, QualifiedNameSyntax)
Return GetTargetTypeName(qualifiedName.Right, Nothing)
Dim qualifiedName = DirectCast(node, QualifiedNameSyntax)
Return TryGetSimpleTypeNameWorker(qualifiedName.Right, Nothing, simpleTypeName)
ElseIf TypeOf typeSyntax Is NullableTypeSyntax Then
Return GetTypeName(DirectCast(typeSyntax, NullableTypeSyntax).ElementType)
ElseIf TypeOf node Is NullableTypeSyntax Then
Return TryGetSimpleTypeNameWorker(DirectCast(node, NullableTypeSyntax).ElementType, typeParameterNames, simpleTypeName)
ElseIf TypeOf typeSyntax Is PredefinedTypeSyntax Then
Return GetSpecialTypeName(DirectCast(typeSyntax, PredefinedTypeSyntax))
ElseIf TypeOf node Is PredefinedTypeSyntax Then
simpleTypeName = GetSpecialTypeName(DirectCast(node, PredefinedTypeSyntax))
Return simpleTypeName IsNot Nothing
End If
Return Nothing
simpleTypeName = Nothing
Return False
End Function
Private Function GetSpecialTypeName(predefinedTypeNode As PredefinedTypeSyntax) As String
Private Shared Function GetSpecialTypeName(predefinedTypeNode As PredefinedTypeSyntax) As String
Select Case predefinedTypeNode.Keyword.Kind()
Case SyntaxKind.BooleanKeyword
Return "Boolean"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册