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

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

@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// <summary> /// <summary>
/// Creates a new C# AST resolver. /// Creates a new C# AST resolver.
/// </summary> /// </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="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 /// <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. /// 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
MemberLookup lookup = CreateMemberLookup(); MemberLookup lookup = CreateMemberLookup();
ResolveResult result = lookup.Lookup(target, identifier, typeArguments, isInvocationTarget); ResolveResult result = lookup.Lookup(target, identifier, typeArguments, isInvocationTarget);
if (result is UnknownMemberResolveResult) { if (result is UnknownMemberResolveResult) {
var extensionMethods = GetExtensionMethods(target.Type, identifier, typeArguments); var extensionMethods = GetExtensionMethods(identifier, typeArguments);
if (extensionMethods.Count > 0) { if (extensionMethods.Count > 0) {
return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) { return new MethodGroupResolveResult(target, identifier, EmptyList<MethodListWithDeclaringType>.Instance, typeArguments) {
extensionMethods = extensionMethods extensionMethods = extensionMethods
@ -1599,12 +1599,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion #endregion
#region GetExtensionMethods #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> /// <summary>
/// Gets the extension methods that are called 'name' /// Gets the extension methods that are called 'name'
/// and are applicable with a first argument type of 'targetType'. /// and are applicable with a first argument type of 'targetType'.
/// </summary> /// </summary>
/// <param name="targetType">Type of the 'this' argument</param> /// <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. /// <param name="typeArguments">Explicitly provided type arguments.
/// An empty list will return all matching extension method definitions; /// An empty list will return all matching extension method definitions;
/// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods /// a non-empty list will return <see cref="SpecializedMethod"/>s for all extension methods
@ -1618,24 +1640,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// new List { all extensions from SomeExtensions } /// new List { all extensions from SomeExtensions }
/// } /// }
/// </remarks> /// </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>>(); List<List<IMethod>> extensionMethodGroups = new List<List<IMethod>>();
foreach (var inputGroup in GetAllExtensionMethods()) { foreach (var inputGroup in GetAllExtensionMethods()) {
List<IMethod> outputGroup = new List<IMethod>(); List<IMethod> outputGroup = new List<IMethod>();
foreach (var method in inputGroup) { foreach (var method in inputGroup) {
if (method.Name != name) if (name != null && method.Name != name)
continue; continue;
if (typeArguments != null && typeArguments.Count > 0) { if (typeArguments != null && typeArguments.Count > 0) {
if (method.TypeParameters.Count != typeArguments.Count) if (method.TypeParameters.Count != typeArguments.Count)
continue; continue;
SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments); SpecializedMethod sm = new SpecializedMethod(method.DeclaringType, method, typeArguments);
// TODO: verify targetType if (IsEligibleExtensionMethod(targetType, method, false))
outputGroup.Add(sm); outputGroup.Add(sm);
} else { } else {
// TODO: verify targetType if (IsEligibleExtensionMethod(targetType, method, true))
outputGroup.Add(method); outputGroup.Add(method);
} }
} }
if (outputGroup.Count > 0) if (outputGroup.Count > 0)
@ -1644,22 +1666,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
return extensionMethodGroups; 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>>(); if (targetType == null)
foreach (var inputGroup in GetAllExtensionMethods()) { return true;
List<IMethod> outputGroup = new List<IMethod>(); if (method.Parameters.Count == 0)
foreach (var method in inputGroup) { return false;
outputGroup.Add(method); 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) thisParameterType = thisParameterType.AcceptVisitor(substitution);
extensionMethodGroups.Add(outputGroup);
} }
return extensionMethodGroups; Conversion c = conversions.ImplicitConversion(targetType, thisParameterType);
return c.IsValid && (c.IsIdentityConversion || c.IsReferenceConversion || c.IsBoxingConversion);
} }
//
/// <summary> /// <summary>
/// Gets all extension methods available in the current using scope. /// Gets all extension methods available in the current using scope.
/// This list includes unaccessible /// This list includes unaccessible

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

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

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

@ -418,36 +418,11 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
if (newParameterizedType != null) { if (newParameterizedType != null) {
// C# 4.0 spec: §4.4.4 Satisfying constraints // C# 4.0 spec: §4.4.4 Satisfying constraints
var typeParameters = newParameterizedType.GetDefinition().TypeParameters; var typeParameters = newParameterizedType.GetDefinition().TypeParameters;
var substitution = newParameterizedType.GetSubstitution();
for (int i = 0; i < typeParameters.Count; i++) { for (int i = 0; i < typeParameters.Count; i++) {
ITypeParameter tp = typeParameters[i]; if (!ValidateConstraints(typeParameters[i], newParameterizedType.GetTypeArgument(i), substitution, conversions)) {
IType typeArg = newParameterizedType.GetTypeArgument(i); ConstraintsValid = false;
switch (typeArg.Kind) { // void, null, and pointers cannot be used as type arguments break;
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);
} }
} }
} }
@ -457,6 +432,64 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
} }
#endregion #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 #region CheckApplicability
void CheckApplicability(Candidate candidate) void CheckApplicability(Candidate candidate)
{ {

Loading…
Cancel
Save