Browse Source

Implemented eligibility check for extension methods.

newNRvisualizers
Daniel Grunwald 14 years ago
parent
commit
9fea0d07fe
  1. 4
      ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
  2. 2
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
  3. 72
      ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
  4. 3
      ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
  5. 91
      ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

4
ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
//
//
// CSharpCompletionEngine.cs
//
// Author:
@ -1778,7 +1778,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion @@ -1778,7 +1778,7 @@ namespace ICSharpCode.NRefactory.CSharp.Completion
}
} else {
foreach (var meths in state.GetAllExtensionMethods (type)) {
foreach (var meths in state.GetExtensionMethods (type)) {
foreach (var m in meths) {
result.AddMember (m);
}

2
ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs

@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Creates a new C# AST resolver.
/// </summary>
/// <param name="resolver">The resolver state at the root node.</param>
/// <param name="resolver">The resolver state at the root node (to be more precise: outside the root node).</param>
/// <param name="rootNode">The root node of the resolved tree.</param>
/// <param name="parsedFile">The parsed file for the nodes being resolved. This parameter is used only
/// when the root node is on the type level; it is not necessary when an expression is passed.

72
ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs

@ -1530,7 +1530,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1530,7 +1530,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
MemberLookup lookup = CreateMemberLookup();
ResolveResult result = lookup.Lookup(target, identifier, typeArguments, isInvocationTarget);
if (result is UnknownMemberResolveResult) {
var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments);
var extensionMethods = GetExtensionMethods(identifier, typeArguments);
if (extensionMethods.Count > 0) {
return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
extensionMethods = extensionMethods
@ -1599,12 +1599,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1599,12 +1599,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion
#region GetExtensionMethods
/// <summary>
/// Gets all extension methods that are available in the current context.
/// </summary>
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
/// <param name="typeArguments">Explicitly provided type arguments.
/// An empty list will return all matching extension method definitions;
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
/// with the matching number of type parameters.</param>
/// <remarks>
/// The results are stored in nested lists because they are grouped by using scope.
/// That is, for "using SomeExtensions; namespace X { using MoreExtensions; ... }",
/// the return value will be
/// new List {
/// new List { all extensions from MoreExtensions },
/// new List { all extensions from SomeExtensions }
/// }
/// </remarks>
public List<List<IMethod>> GetExtensionMethods(string name = null, IList<IType> typeArguments = null)
{
return GetExtensionMethods(null, name, typeArguments);
}
/// <summary>
/// Gets the extension methods that are called 'name'
/// and are applicable with a first argument type of 'targetType'.
/// </summary>
/// <param name="targetType">Type of the 'this' argument</param>
/// <param name="name">Name of the extension method</param>
/// <param name="name">Name of the extension method. Pass null to retrieve all extension methods.</param>
/// <param name="typeArguments">Explicitly provided type arguments.
/// An empty list will return all matching extension method definitions;
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
@ -1618,24 +1640,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1618,24 +1640,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// new List { all extensions from SomeExtensions }
/// }
/// </remarks>
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name, IList<IType> typeArguments = null)
public List<List<IMethod>> GetExtensionMethods(IType targetType, string name = null, IList<IType> typeArguments = null)
{
List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
foreach (var inputGroup in GetAllExtensionMethods()) {
List<IMethod> outputGroup = new List<IMethod>();
foreach (var method in inputGroup) {
if (method.Name != name)
if (name != null && method.Name != name)
continue;
if (typeArguments != null && typeArguments.Count > 0) {
if (method.TypeParameters.Count != typeArguments.Count)
continue;
SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments);
// TODO: verify targetType
outputGroup.Add(sm);
if (IsEligibleExtensionMethod(targetType, method, false))
outputGroup.Add(sm);
} else {
// TODO: verify targetType
outputGroup.Add(method);
if (IsEligibleExtensionMethod(targetType, method, true))
outputGroup.Add(method);
}
}
if (outputGroup.Count > 0)
@ -1644,22 +1666,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -1644,22 +1666,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return extensionMethodGroups;
}
public List<List<IMethod>> GetAllExtensionMethods(IType targetType)
bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference)
{
List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
foreach (var inputGroup in GetAllExtensionMethods()) {
List<IMethod> outputGroup = new List<IMethod>();
foreach (var method in inputGroup) {
outputGroup.Add(method);
if (targetType == null)
return true;
if (method.Parameters.Count == 0)
return false;
IType thisParameterType = method.Parameters[0].Type;
if (useTypeInference && method.TypeParameters.Count > 0) {
// We need to infer type arguments from targetType:
TypeInference ti = new TypeInference(compilation, conversions);
ResolveResult[] arguments = { new ResolveResult(targetType) };
IType[] parameterTypes = { method.Parameters[0].Type };
bool success;
IType[] inferredTypes = ti.InferTypeArguments(method.TypeParameters, arguments, parameterTypes, out success);
var substitution = new TypeParameterSubstitution(null, inferredTypes);
// Validate that the types that could be inferred (aren't unknown) satisfy the constraints:
for (int i = 0; i < inferredTypes.Length; i++) {
if (inferredTypes[i].Kind != TypeKind.Unknown && inferredTypes[i].Kind != TypeKind.UnboundTypeArgument) {
if (!OverloadResolution.ValidateConstraints(method.TypeParameters[i], inferredTypes[i], substitution, conversions))
return false;
}
}
if (outputGroup.Count > 0)
extensionMethodGroups.Add(outputGroup);
thisParameterType = thisParameterType.AcceptVisitor(substitution);
}
return extensionMethodGroups;
Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion);
}
//
/// <summary>
/// Gets all extension methods available in the current using scope.
/// This list includes unaccessible

3
ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs

@ -126,6 +126,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -126,6 +126,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary>
/// Gets all candidate extension methods.
/// Note: this includes candidates that are not eligible due to a
/// </summary>
/// <remarks>
/// The results are stored in nested lists because they are grouped by using scope.
@ -141,7 +142,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -141,7 +142,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (resolver != null) {
Debug.Assert(extensionMethods == null);
try {
extensionMethods = resolver.GetExtensionMethods(this.TargetType, methodName, typeArguments);
extensionMethods = resolver.GetExtensionMethods(methodName, typeArguments);
} finally {
resolver = null;
}

91
ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs

@ -418,36 +418,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -418,36 +418,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (newParameterizedType != null) {
// C# 4.0 spec: §4.4.4 Satisfying constraints
var typeParameters = newParameterizedType.GetDefinition().TypeParameters;
var substitution = newParameterizedType.GetSubstitution();
for (int i = 0; i < typeParameters.Count; i++) {
ITypeParameter tp = typeParameters[i];
IType typeArg = newParameterizedType.GetTypeArgument(i);
switch (typeArg.Kind) { // void, null, and pointers cannot be used as type arguments
case TypeKind.Void:
case TypeKind.Null:
case TypeKind.Pointer:
ConstraintsValid = false;
break;
}
if (tp.HasReferenceTypeConstraint) {
if (typeArg.IsReferenceType != true)
ConstraintsValid = false;
}
if (tp.HasValueTypeConstraint) {
if (!NullableType.IsNonNullableValueType(typeArg))
ConstraintsValid = false;
}
if (tp.HasDefaultConstructorConstraint) {
ITypeDefinition def = typeArg.GetDefinition();
if (def != null && def.IsAbstract)
ConstraintsValid = false;
ConstraintsValid &= typeArg.GetConstructors(
m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
).Any();
}
foreach (IType constraintType in tp.DirectBaseTypes) {
IType c = constraintType.AcceptVisitor(newParameterizedType.GetSubstitution());
ConstraintsValid &= conversions.IsConstraintConvertible(typeArg, c);
if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) {
ConstraintsValid = false;
break;
}
}
}
@ -457,6 +432,64 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver @@ -457,6 +432,64 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
}
#endregion
#region Validate Constraints
/// <summary>
/// Validates whether the given type argument satisfies the constraints for the given type parameter.
/// </summary>
/// <param name="typeParameter">The type parameter.</param>
/// <param name="typeArgument">The type argument.</param>
/// <param name="substitution">The substitution that defines how type parameters are replaced with type arguments.
/// The substitution is used to check constraints that depend on other type parameters (or recursively on the same type parameter).</param>
/// <returns>True if the constraints are satisfied; false otherwise.</returns>
public static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution)
{
if (typeParameter == null)
throw new ArgumentNullException("typeParameter");
if (typeParameter.Owner == null)
throw new ArgumentNullException("typeParameter.Owner");
if (typeArgument == null)
throw new ArgumentNullException("typeArgument");
return ValidateConstraints(typeParameter, typeArgument, substitution, Conversions.Get(typeParameter.Owner.Compilation));
}
internal static bool ValidateConstraints(ITypeParameter typeParameter, IType typeArgument, TypeVisitor substitution, Conversions conversions)
{
switch (typeArgument.Kind) { // void, null, and pointers cannot be used as type arguments
case TypeKind.Void:
case TypeKind.Null:
case TypeKind.Pointer:
return false;
}
if (typeParameter.HasReferenceTypeConstraint) {
if (typeArgument.IsReferenceType != true)
return false;
}
if (typeParameter.HasValueTypeConstraint) {
if (!NullableType.IsNonNullableValueType(typeArgument))
return false;
}
if (typeParameter.HasDefaultConstructorConstraint) {
ITypeDefinition def = typeArgument.GetDefinition();
if (def != null && def.IsAbstract)
return false;
var ctors = typeArgument.GetConstructors(
m => m.Parameters.Count == 0 && m.Accessibility == Accessibility.Public,
GetMemberOptions.IgnoreInheritedMembers | GetMemberOptions.ReturnMemberDefinitions
);
if (!ctors.Any())
return false;
}
foreach (IType constraintType in typeParameter.DirectBaseTypes) {
IType c = constraintType;
if (substitution != null)
c = c.AcceptVisitor(substitution);
if (!conversions.IsConstraintConvertible(typeArgument, c))
return false;
}
return true;
}
#endregion
#region CheckApplicability
void CheckApplicability(Candidate candidate)
{

Loading…
Cancel
Save