diff --git a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
index 21ca4f8169..d097d9c89b 100644
--- a/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
+++ b/ICSharpCode.NRefactory.CSharp/Completion/CSharpCompletionEngine.cs
@@ -1,4 +1,4 @@
-//
+//
// CSharpCompletionEngine.cs
//
// Author:
@@ -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);
}
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
index 3efd3abe88..23bc165629 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpAstResolver.cs
@@ -59,7 +59,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
///
/// Creates a new C# AST resolver.
///
- /// The resolver state at the root node.
+ /// The resolver state at the root node (to be more precise: outside the root node).
/// The root node of the resolved tree.
/// 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.
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
index a3a3ff2a2a..4a8adf7fcc 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/CSharpResolver.cs
@@ -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.Instance, typeArguments) {
extensionMethods = extensionMethods
@@ -1599,12 +1599,34 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
#endregion
#region GetExtensionMethods
+ ///
+ /// Gets all extension methods that are available in the current context.
+ ///
+ /// Name of the extension method. Pass null to retrieve all extension methods.
+ /// Explicitly provided type arguments.
+ /// An empty list will return all matching extension method definitions;
+ /// a non-empty list will return s for all extension methods
+ /// with the matching number of type parameters.
+ ///
+ /// 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 }
+ /// }
+ ///
+ public List> GetExtensionMethods(string name = null, IList typeArguments = null)
+ {
+ return GetExtensionMethods(null, name, typeArguments);
+ }
+
///
/// Gets the extension methods that are called 'name'
/// and are applicable with a first argument type of 'targetType'.
///
/// Type of the 'this' argument
- /// Name of the extension method
+ /// Name of the extension method. Pass null to retrieve all extension methods.
/// Explicitly provided type arguments.
/// An empty list will return all matching extension method definitions;
/// a non-empty list will return s for all extension methods
@@ -1618,24 +1640,24 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
/// new List { all extensions from SomeExtensions }
/// }
///
- public List> GetExtensionMethods(IType targetType, string name, IList typeArguments = null)
+ public List> GetExtensionMethods(IType targetType, string name = null, IList typeArguments = null)
{
List> extensionMethodGroups = new List>();
foreach (var inputGroup in GetAllExtensionMethods()) {
List outputGroup = new List();
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
return extensionMethodGroups;
}
- public List> GetAllExtensionMethods(IType targetType)
+ bool IsEligibleExtensionMethod(IType targetType, IMethod method, bool useTypeInference)
{
- List> extensionMethodGroups = new List>();
- foreach (var inputGroup in GetAllExtensionMethods()) {
- List outputGroup = new List();
- 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);
}
- //
-
///
/// Gets all extension methods available in the current using scope.
/// This list includes unaccessible
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
index 6e40f64ba2..b04b6b77d3 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/MethodGroupResolveResult.cs
@@ -126,6 +126,7 @@ namespace ICSharpCode.NRefactory.CSharp.Resolver
///
/// Gets all candidate extension methods.
+ /// Note: this includes candidates that are not eligible due to a
///
///
/// 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) {
Debug.Assert(extensionMethods == null);
try {
- extensionMethods = resolver.GetExtensionMethods(this.TargetType, methodName, typeArguments);
+ extensionMethods = resolver.GetExtensionMethods(methodName, typeArguments);
} finally {
resolver = null;
}
diff --git a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
index 8123aaf5d2..9b661b35df 100644
--- a/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
+++ b/ICSharpCode.NRefactory.CSharp/Resolver/OverloadResolution.cs
@@ -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
}
#endregion
+ #region Validate Constraints
+ ///
+ /// Validates whether the given type argument satisfies the constraints for the given type parameter.
+ ///
+ /// The type parameter.
+ /// The type argument.
+ /// 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).
+ /// True if the constraints are satisfied; false otherwise.
+ 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)
{