提交 3feb2bbe 编写于 作者: C CyrusNajmabadi

Infer better types for methods/properties if they have names that indicates they're a boolean type.

上级 de989afb
......@@ -7307,5 +7307,61 @@ public void M1()
}",
parseOptions: TestOptions.Regular);
}
[WorkItem(15315, "https://github.com/dotnet/roslyn/issues/15315")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
public async Task TestInferBooleanTypeBasedOnName1()
{
await TestAsync(
@"class Class
{
void Method(int i)
{
var v = [|IsPrime|](i);
}
}",
@"using System;
class Class
{
void Method(int i)
{
var v = IsPrime(i);
}
private bool IsPrime(int i)
{
throw new NotImplementedException();
}
}");
}
[WorkItem(15315, "https://github.com/dotnet/roslyn/issues/15315")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)]
public async Task TestInferBooleanTypeBasedOnName2()
{
await TestAsync(
@"class Class
{
void Method(int i)
{
var v = [|Issue|](i);
}
}",
@"using System;
class Class
{
void Method(int i)
{
var v = Issue(i);
}
private object Issue(int i)
{
throw new NotImplementedException();
}
}");
}
}
}
\ No newline at end of file
// 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.Collections.Immutable;
using System.Composition;
using System.Linq;
......@@ -13,6 +11,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Simplification;
using Roslyn.Utilities;
......@@ -175,4 +174,4 @@ private static SyntaxNode ConvertToAwaitExpression(ExpressionSyntax expression)
.WithAdditionalAnnotations(Simplifier.Annotation, Formatter.Annotation);
}
}
}
}
\ No newline at end of file
......@@ -41,8 +41,9 @@ protected override ITypeSymbol DetermineReturnTypeWorker(CancellationToken cance
// Defer to the type inferrer to figure out what the return type of this new method
// should be.
var typeInference = this.Document.Project.LanguageServices.GetService<ITypeInferenceService>();
var inferredType = typeInference.InferType(this.Document.SemanticModel,
_invocationExpression, objectAsDefault: true, cancellationToken: cancellationToken);
var inferredType = typeInference.InferType(
this.Document.SemanticModel, _invocationExpression, objectAsDefault: true,
nameOpt: this.State.IdentifierToken.ValueText, cancellationToken: cancellationToken);
return inferredType;
}
......
......@@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.SignatureHelp
{
......
......@@ -269,9 +269,9 @@ internal bool CanGenerateLocal()
{
var typeInference = document.Project.LanguageServices.GetService<ITypeInferenceService>();
var inferredType = typeInference.InferType(
document.SemanticModel, this.SimpleNameOrMemberAccessExpressionOpt,
objectAsDefault: true,
cancellationToken: cancellationToken);
document.SemanticModel, this.SimpleNameOrMemberAccessExpressionOpt, objectAsDefault: true,
nameOpt: this.IdentifierToken.ValueText, cancellationToken: cancellationToken);
var compilation = document.SemanticModel.Compilation;
inferredType = inferredType.SpecialType == SpecialType.System_Void
? compilation.ObjectType
......
......@@ -47,8 +47,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateMethod
End Select
Dim typeInference = Document.Project.LanguageServices.GetService(Of ITypeInferenceService)()
Dim inferredType = typeInference.InferType(Document.SemanticModel, Me.InvocationExpression,
objectAsDefault:=True, cancellationToken:=cancellationToken)
Dim inferredType = typeInference.InferType(
Document.SemanticModel, Me.InvocationExpression, objectAsDefault:=True,
nameOpt:=Me.State.IdentifierToken.ValueText, cancellationToken:=cancellationToken)
Return inferredType
End Function
......
......@@ -751,8 +751,8 @@ private IEnumerable<TypeInferenceInfo> InferTypeInArrayType(ArrayTypeSyntax arra
var currentTypes = InferTypes(arrayType);
for (var i = 0; i < arrayType.RankSpecifiers.Count; i++)
{
currentTypes = currentTypes.Where(c => c.InferredType is IArrayTypeSymbol)
.Select(c => new TypeInferenceInfo(((IArrayTypeSymbol)c.InferredType).ElementType));
currentTypes = currentTypes.WhereAsArray(c => c.InferredType is IArrayTypeSymbol)
.SelectAsArray(c => new TypeInferenceInfo(((IArrayTypeSymbol)c.InferredType).ElementType));
}
return currentTypes;
}
......
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.LanguageServices.TypeInferenceService
{
internal partial class AbstractTypeInferenceService<TExpressionSyntax> : ITypeInferenceService
where TExpressionSyntax : SyntaxNode
{
protected abstract class AbstractTypeInferrer
{
protected readonly CancellationToken CancellationToken;
protected readonly SemanticModel SemanticModel;
protected readonly Func<TypeInferenceInfo, bool> IsUsableTypeFunc;
private readonly HashSet<TExpressionSyntax> _seenExpressionInferType = new HashSet<TExpressionSyntax>();
private readonly HashSet<TExpressionSyntax> _seenExpressionGetType = new HashSet<TExpressionSyntax>();
private static readonly Func<TypeInferenceInfo, bool> s_isNotNull = t => t.InferredType != null;
protected AbstractTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken)
{
this.SemanticModel = semanticModel;
this.CancellationToken = cancellationToken;
this.IsUsableTypeFunc = t => t.InferredType != null && !IsUnusableType(t.InferredType);
}
protected abstract IEnumerable<TypeInferenceInfo> InferTypesWorker_DoNotCallDirectly(int position);
protected abstract IEnumerable<TypeInferenceInfo> InferTypesWorker_DoNotCallDirectly(TExpressionSyntax expression);
protected abstract IEnumerable<TypeInferenceInfo> GetTypes_DoNotCallDirectly(TExpressionSyntax expression, bool objectAsDefault);
protected abstract bool IsUnusableType(ITypeSymbol arg);
protected Compilation Compilation => SemanticModel.Compilation;
public ImmutableArray<TypeInferenceInfo> InferTypes(int position)
{
var types = InferTypesWorker_DoNotCallDirectly(position);
return Filter(types);
}
public ImmutableArray<TypeInferenceInfo> InferTypes(TExpressionSyntax expression, bool filterUnusable = true)
{
if (expression != null)
{
if (_seenExpressionInferType.Add(expression))
{
var types = InferTypesWorker_DoNotCallDirectly(expression);
return Filter(types, filterUnusable);
}
}
return ImmutableArray<TypeInferenceInfo>.Empty;
}
protected IEnumerable<TypeInferenceInfo> GetTypes(TExpressionSyntax expression, bool objectAsDefault = false)
{
if (_seenExpressionGetType.Add(expression))
{
return GetTypes_DoNotCallDirectly(expression, objectAsDefault);
}
return SpecializedCollections.EmptyEnumerable<TypeInferenceInfo>();
}
private ImmutableArray<TypeInferenceInfo> Filter(IEnumerable<TypeInferenceInfo> types, bool filterUnusable = true)
{
return types.Where(filterUnusable ? IsUsableTypeFunc : s_isNotNull)
.Distinct()
.ToImmutableArray();
}
protected IEnumerable<ITypeSymbol> ExpandParamsParameter(IParameterSymbol parameterSymbol)
{
var result = new List<ITypeSymbol>();
result.Add(parameterSymbol.Type);
if (parameterSymbol.IsParams)
{
var arrayTypeSymbol = parameterSymbol.Type as IArrayTypeSymbol;
if (arrayTypeSymbol != null)
{
result.Add(arrayTypeSymbol.ElementType);
}
}
return result;
}
}
}
}
......@@ -2,123 +2,120 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.LanguageServices.TypeInferenceService
{
internal abstract class AbstractTypeInferenceService<TExpressionSyntax> : ITypeInferenceService
internal abstract partial class AbstractTypeInferenceService<TExpressionSyntax> : ITypeInferenceService
where TExpressionSyntax : SyntaxNode
{
protected abstract class AbstractTypeInferrer
{
protected readonly CancellationToken CancellationToken;
protected readonly SemanticModel SemanticModel;
protected readonly Func<TypeInferenceInfo, bool> IsUsableTypeFunc;
private readonly HashSet<TExpressionSyntax> _seenExpressionInferType = new HashSet<TExpressionSyntax>();
private readonly HashSet<TExpressionSyntax> _seenExpressionGetType = new HashSet<TExpressionSyntax>();
private static readonly Func<TypeInferenceInfo, bool> s_isNotNull = t => t.InferredType != null;
protected abstract AbstractTypeInferrer CreateTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken);
protected AbstractTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken)
private ImmutableArray<ITypeSymbol> InferTypeBasedOnNameIfEmpty(
SemanticModel semanticModel, ImmutableArray<ITypeSymbol> result, string nameOpt)
{
if (result.IsEmpty && nameOpt != null)
{
this.SemanticModel = semanticModel;
this.CancellationToken = cancellationToken;
this.IsUsableTypeFunc = t => t.InferredType != null && !IsUnusableType(t.InferredType);
return InferTypeBasedOnName(semanticModel, nameOpt);
}
protected abstract IEnumerable<TypeInferenceInfo> InferTypesWorker_DoNotCallDirectly(int position);
protected abstract IEnumerable<TypeInferenceInfo> InferTypesWorker_DoNotCallDirectly(TExpressionSyntax expression);
protected abstract IEnumerable<TypeInferenceInfo> GetTypes_DoNotCallDirectly(TExpressionSyntax expression, bool objectAsDefault);
protected abstract bool IsUnusableType(ITypeSymbol arg);
protected Compilation Compilation => SemanticModel.Compilation;
return result;
}
public IEnumerable<TypeInferenceInfo> InferTypes(int position)
private ImmutableArray<TypeInferenceInfo> InferTypeBasedOnNameIfEmpty(
SemanticModel semanticModel, ImmutableArray<TypeInferenceInfo> result, string nameOpt)
{
if (result.IsEmpty && nameOpt != null)
{
var types = InferTypesWorker_DoNotCallDirectly(position);
return Filter(types);
var types = InferTypeBasedOnName(semanticModel, nameOpt);
return types.SelectAsArray(t => new TypeInferenceInfo(t));
}
public IEnumerable<TypeInferenceInfo> InferTypes(TExpressionSyntax expression, bool filterUnusable = true)
{
if (expression != null)
{
if (_seenExpressionInferType.Add(expression))
{
var types = InferTypesWorker_DoNotCallDirectly(expression);
return Filter(types, filterUnusable);
}
}
return result;
}
return SpecializedCollections.EmptyEnumerable<TypeInferenceInfo>();
}
private static readonly ImmutableArray<string> s_booleanPrefixes =
ImmutableArray.Create("Is", "Has", "Contains", "Supports");
private ImmutableArray<ITypeSymbol> InferTypeBasedOnName(
SemanticModel semanticModel, string name)
{
var matchesBoolean = MatchesBoolean(name);
return matchesBoolean
? ImmutableArray.Create<ITypeSymbol>(semanticModel.Compilation.GetSpecialType(SpecialType.System_Boolean))
: ImmutableArray<ITypeSymbol>.Empty;
}
protected IEnumerable<TypeInferenceInfo> GetTypes(TExpressionSyntax expression, bool objectAsDefault = false)
private static bool MatchesBoolean(string name)
{
foreach (var prefix in s_booleanPrefixes)
{
if (_seenExpressionGetType.Add(expression))
if (Matches(name, prefix))
{
return GetTypes_DoNotCallDirectly(expression, objectAsDefault);
return true;
}
return SpecializedCollections.EmptyEnumerable<TypeInferenceInfo>();
}
private IEnumerable<TypeInferenceInfo> Filter(IEnumerable<TypeInferenceInfo> types, bool filterUnusable = true)
{
return types.Where(filterUnusable ? IsUsableTypeFunc : s_isNotNull)
.Distinct()
.ToImmutableReadOnlyListOrEmpty();
}
return false;
}
protected IEnumerable<ITypeSymbol> ExpandParamsParameter(IParameterSymbol parameterSymbol)
private static bool Matches(string name, string prefix)
{
if (name.StartsWith(prefix))
{
var result = new List<ITypeSymbol>();
result.Add(parameterSymbol.Type);
if (parameterSymbol.IsParams)
if (name.Length == prefix.Length)
{
var arrayTypeSymbol = parameterSymbol.Type as IArrayTypeSymbol;
if (arrayTypeSymbol != null)
{
result.Add(arrayTypeSymbol.ElementType);
}
return true;
}
return result;
var nextChar = name[prefix.Length];
return !char.IsLower(nextChar);
}
}
protected abstract AbstractTypeInferrer CreateTypeInferrer(SemanticModel semanticModel, CancellationToken cancellationToken);
return false;
}
public IEnumerable<ITypeSymbol> InferTypes(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
public ImmutableArray<ITypeSymbol> InferTypes(
SemanticModel semanticModel, int position,
string nameOpt, CancellationToken cancellationToken)
{
return CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(position).Select(t => t.InferredType);
var result = CreateTypeInferrer(semanticModel, cancellationToken)
.InferTypes(position)
.Select(t => t.InferredType)
.ToImmutableArray();
return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt);
}
public IEnumerable<ITypeSymbol> InferTypes(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
public ImmutableArray<ITypeSymbol> InferTypes(
SemanticModel semanticModel, SyntaxNode expression,
string nameOpt, CancellationToken cancellationToken)
{
var result = new List<ITypeSymbol>();
var typeInferenceInfo = CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(expression as TExpressionSyntax);
foreach (var info in typeInferenceInfo)
{
result.Add(info.InferredType);
}
var result = CreateTypeInferrer(semanticModel, cancellationToken)
.InferTypes(expression as TExpressionSyntax)
.Select(info => info.InferredType)
.ToImmutableArray();
return result;
return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt);
}
public IEnumerable<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
public ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(
SemanticModel semanticModel, int position,
string nameOpt, CancellationToken cancellationToken)
{
return CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(position);
var result = CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(position);
return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt);
}
public IEnumerable<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
public ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(
SemanticModel semanticModel, SyntaxNode expression,
string nameOpt, CancellationToken cancellationToken)
{
return CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(expression as TExpressionSyntax);
var result = CreateTypeInferrer(semanticModel, cancellationToken).InferTypes(expression as TExpressionSyntax);
return InferTypeBasedOnNameIfEmpty(semanticModel, result, nameOpt);
}
}
}
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Host;
......@@ -22,12 +23,11 @@ namespace Microsoft.CodeAnalysis.LanguageServices
/// </summary>
internal interface ITypeInferenceService : ILanguageService
{
IEnumerable<ITypeSymbol> InferTypes(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken);
IEnumerable<ITypeSymbol> InferTypes(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
ImmutableArray<ITypeSymbol> InferTypes(SemanticModel semanticModel, SyntaxNode expression, string nameOpt, CancellationToken cancellationToken);
ImmutableArray<ITypeSymbol> InferTypes(SemanticModel semanticModel, int position, string nameOpt, CancellationToken cancellationToken);
IEnumerable<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
IEnumerable<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken);
ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, int position, string nameOpt, CancellationToken cancellationToken);
ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(SemanticModel semanticModel, SyntaxNode expression, string nameOpt, CancellationToken cancellationToken);
}
internal struct TypeInferenceInfo
......
// 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.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.LanguageServices;
......@@ -9,6 +10,18 @@ namespace Microsoft.CodeAnalysis.Shared.Extensions
{
internal static class ITypeInferenceServiceExtensions
{
public static ImmutableArray<ITypeSymbol> InferTypes(this ITypeInferenceService service, SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> service.InferTypes(semanticModel, expression, nameOpt: null, cancellationToken: cancellationToken);
public static ImmutableArray<ITypeSymbol> InferTypes(this ITypeInferenceService service, SemanticModel semanticModel, int position, CancellationToken cancellationToken)
=> service.InferTypes(semanticModel, position, nameOpt: null, cancellationToken: cancellationToken);
public static ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(this ITypeInferenceService service, SemanticModel semanticModel, int position, CancellationToken cancellationToken)
=> service.GetTypeInferenceInfo(semanticModel, position, nameOpt: null, cancellationToken: cancellationToken);
public static ImmutableArray<TypeInferenceInfo> GetTypeInferenceInfo(this ITypeInferenceService service, SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> service.GetTypeInferenceInfo(semanticModel, expression, nameOpt: null, cancellationToken: cancellationToken);
public static INamedTypeSymbol InferDelegateType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
......@@ -20,16 +33,29 @@ internal static class ITypeInferenceServiceExtensions
return delegateTypes.WhereNotNull().FirstOrDefault();
}
public static ITypeSymbol InferType(this ITypeInferenceService typeInferenceService,
public static ITypeSymbol InferType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
SyntaxNode expression,
bool objectAsDefault,
CancellationToken cancellationToken)
{
return InferType(
typeInferenceService, semanticModel, expression, objectAsDefault,
nameOpt: null, cancellationToken: cancellationToken);
}
public static ITypeSymbol InferType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
SyntaxNode expression,
bool objectAsDefault,
string nameOpt,
CancellationToken cancellationToken)
{
var types = typeInferenceService.InferTypes(semanticModel, expression, cancellationToken)
.WhereNotNull();
var types = typeInferenceService.InferTypes(semanticModel, expression, nameOpt, cancellationToken);
if (!types.Any())
if (types.Length == 0)
{
return objectAsDefault ? semanticModel.Compilation.ObjectType : null;
}
......@@ -37,16 +63,29 @@ internal static class ITypeInferenceServiceExtensions
return types.FirstOrDefault();
}
public static ITypeSymbol InferType(this ITypeInferenceService typeInferenceService,
public static ITypeSymbol InferType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
int position,
bool objectAsDefault,
CancellationToken cancellationToken)
{
return InferType(
typeInferenceService, semanticModel, position, objectAsDefault,
nameOpt: null, cancellationToken: cancellationToken);
}
public static ITypeSymbol InferType(
this ITypeInferenceService typeInferenceService,
SemanticModel semanticModel,
int position,
bool objectAsDefault,
string nameOpt,
CancellationToken cancellationToken)
{
var types = typeInferenceService.InferTypes(semanticModel, position, cancellationToken)
.WhereNotNull();
var types = typeInferenceService.InferTypes(semanticModel, position, nameOpt, cancellationToken);
if (!types.Any())
if (types.Length == 0)
{
return objectAsDefault ? semanticModel.Compilation.ObjectType : null;
}
......@@ -54,4 +93,4 @@ internal static class ITypeInferenceServiceExtensions
return types.FirstOrDefault();
}
}
}
}
\ No newline at end of file
......@@ -360,6 +360,7 @@
<Compile Include="Execution\SolutionAsset.cs" />
<Compile Include="Execution\WellKnownSynchronizationKinds.cs" />
<Compile Include="Experiments\IExperimentationService.cs" />
<Compile Include="LanguageServices\TypeInferenceService\AbstractTypeInferenceService.AbstractTypeInferrer.cs" />
<Compile Include="PatternMatching\PatternMatch.cs" />
<Compile Include="PatternMatching\PatternMatcher.cs" />
<Compile Include="PatternMatching\PatternMatcher.Segment.cs" />
......
......@@ -352,8 +352,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Dim currentTypes = InferTypes(arrayType)
Dim i = 0
While i < arrayType.RankSpecifiers.Count
currentTypes = currentTypes.Where(Function(c) TypeOf c.InferredType Is IArrayTypeSymbol) _
.Select(Function(c) New TypeInferenceInfo(DirectCast(c.InferredType, IArrayTypeSymbol).ElementType))
currentTypes = currentTypes.WhereAsArray(Function(c) TypeOf c.InferredType Is IArrayTypeSymbol).
SelectAsArray(Function(c) New TypeInferenceInfo(DirectCast(c.InferredType, IArrayTypeSymbol).ElementType))
i = i + 1
End While
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册