/// <summary>
/// Find the Applicable Function Members (7.4.2.1)
///
/// me: Method Group expression with the members to select.
/// it might contain constructors or methods (or anything
/// that maps to a method).
///
/// Arguments: ArrayList containing resolved Argument objects.
///
/// loc: The location if we want an error to be reported, or a Null
/// location for "probing" purposes.
///
/// Returns: The MethodBase (either a ConstructorInfo or a MethodInfo)
/// that is the best match of me on Arguments.
///
/// </summary>
public virtual MethodGroupExpr OverloadResolve(ResolveContext ec, ref Arguments Arguments,
bool may_fail, Location loc)
{
var candidates = new List<MethodSpec> (2);
List<MethodSpec> params_candidates = null;
int arg_count = Arguments != null ? Arguments.Count : 0;
Dictionary<MethodSpec, Arguments> candidates_expanded = null;
Arguments candidate_args = Arguments;
if (RootContext.Version == LanguageVersion.ISO_1 && Name == "Invoke" && TypeManager.IsDelegateType (DeclaringType)) {
if (!may_fail)
ec.Report.Error (1533, loc, "Invoke cannot be called directly on a delegate");
return null;
}
//
// Enable message recording, it's used mainly by lambda expressions
//
var msg_recorder = new SessionReportPrinter ();
var prev_recorder = ec.Report.SetPrinter (msg_recorder);
do {
//
// Methods in a base class are not candidates if any method in a derived
// class is applicable
//
int best_candidate_rate = int.MaxValue;
foreach (var member in Methods) {
var m = member as MethodSpec;
if (m == null) {
// TODO: It's wrong when non-member is before applicable method
// TODO: Should report only when at least 1 from the batch is applicable
if (candidates.Count != 0) {
ec.Report.SymbolRelatedToPreviousError (candidates [0]);
ec.Report.SymbolRelatedToPreviousError (member);
ec.Report.Warning (467, 2, loc, "Ambiguity between method `{0}' and non-method `{1}'. Using method `{0}'",
candidates[0].GetSignatureForError (), member.GetSignatureForError ());
}
continue;
}
//
// Check if candidate is applicable (section 14.4.2.1)
//
bool params_expanded_form = false;
int candidate_rate = IsApplicable (ec, ref candidate_args, arg_count, ref m, ref params_expanded_form);
if (candidate_rate < best_candidate_rate) {
best_candidate_rate = candidate_rate;
best_candidate = m;
}
if (params_expanded_form) {
if (params_candidates == null)
params_candidates = new List<MethodSpec> (2);
params_candidates.Add (m);
}
if (candidate_args != Arguments) {
if (candidates_expanded == null)
candidates_expanded = new Dictionary<MethodSpec, Arguments> (2);
candidates_expanded.Add (m, candidate_args);
candidate_args = Arguments;
}
if (candidate_rate != 0 || has_inaccessible_candidates_only) {
if (msg_recorder != null)
msg_recorder.EndSession ();
continue;
}
msg_recorder = null;
candidates.Add (m);
}
} while (candidates.Count == 0 && GetBaseTypeMethods (ec));
ec.Report.SetPrinter (prev_recorder);
if (msg_recorder != null && !msg_recorder.IsEmpty) {
if (!may_fail)
msg_recorder.Merge (prev_recorder);
return null;
}
int candidate_top = candidates.Count;
if (candidate_top == 0) {
//
// When we found a top level method which does not match and it's
// not an extension method. We start extension methods lookup from here
//
if (InstanceExpression != null) {
var first = Methods.First ();
var arity = type_arguments == null ? -1 : type_arguments.Count;
ExtensionMethodGroupExpr ex_method_lookup = ec.LookupExtensionMethod (type, first.Name, arity, loc);
if (ex_method_lookup != null) {
ex_method_lookup.ExtensionExpression = InstanceExpression;
ex_method_lookup.SetTypeArguments (ec, type_arguments);
return ex_method_lookup.OverloadResolve (ec, ref Arguments, may_fail, loc);
}
}
if (may_fail)
return null;
//
// Okay so we have failed to find exact match so we
// return error info about the closest match
//
if (best_candidate != null) {
if (CustomErrorHandler != null && !has_inaccessible_candidates_only && CustomErrorHandler.NoExactMatch (ec, best_candidate))
return null;
bool params_expanded = params_candidates != null && params_candidates.Contains (best_candidate);
if (NoExactMatch (ec, ref Arguments, params_expanded))
return null;
}
//
// We failed to find any method with correct argument count
//
if (Methods.First ().Kind == MemberKind.Constructor) {
ec.Report.SymbolRelatedToPreviousError (queried_type);
ec.Report.Error (1729, loc,
"The type `{0}' does not contain a constructor that takes `{1}' arguments",
TypeManager.CSharpName (queried_type), arg_count.ToString ());
} else {
Error_ArgumentCountWrong (ec, arg_count);
}
return null;
}
if (arg_count != 0 && Arguments.HasDynamic) {
best_candidate = null;
return this;
}
//
// Now we actually find the best method
//
best_candidate = candidates [0];
bool method_params = params_candidates != null && params_candidates.Contains (best_candidate);
for (int ix = 1; ix < candidate_top; ix++) {
var candidate = candidates [ix];
if (candidate == best_candidate)
continue;
bool cand_params = params_candidates != null && params_candidates.Contains (candidate);
if (candidates_expanded != null && candidates_expanded.ContainsKey (candidate)) {
candidate_args = candidates_expanded[candidate];
arg_count = candidate_args.Count;
}
if (BetterFunction (ec, candidate_args, arg_count,
candidate, cand_params,
best_candidate, method_params)) {
best_candidate = candidate;
method_params = cand_params;
}
if (candidate_args != Arguments) {
candidate_args = Arguments;
arg_count = candidate_args != null ? candidate_args.Count : 0;
}
}
if (candidates_expanded != null && candidates_expanded.ContainsKey (best_candidate)) {
candidate_args = candidates_expanded[best_candidate];
arg_count = candidate_args.Count;
}
//
// Now check that there are no ambiguities i.e the selected method
// should be better than all the others
//
MethodSpec ambiguous = null;
for (int ix = 1; ix < candidate_top; ix++) {
var candidate = candidates [ix];
if (candidate == best_candidate)
continue;
bool cand_params = params_candidates != null && params_candidates.Contains (candidate);
if (!BetterFunction (ec, candidate_args, arg_count,
best_candidate, method_params,
candidate, cand_params))
{
if (!may_fail)
ec.Report.SymbolRelatedToPreviousError (candidate);
ambiguous = candidate;
}
}
if (ambiguous != null) {
Error_AmbiguousCall (ec, ambiguous);
return this;
}
//
// And now check if the arguments are all
// compatible, perform conversions if
// necessary etc. and return if everything is
// all right
//
if (!VerifyArgumentsCompat (ec, ref candidate_args, arg_count, best_candidate,
method_params, may_fail, loc))
return null;
if (best_candidate == null)
return null;
if (best_candidate.IsGeneric) {
ConstraintChecker.CheckAll (best_candidate.GetGenericMethodDefinition (), best_candidate.TypeArguments,
best_candidate.Constraints, loc, ec.Report);
}
//
// Check ObsoleteAttribute on the best method
//
ObsoleteAttribute oa = best_candidate.GetAttributeObsolete ();
if (oa != null && !ec.IsObsolete)
AttributeTester.Report_ObsoleteMessage (oa, GetSignatureForError (), loc, ec.Report);
best_candidate.MemberDefinition.SetIsUsed ();
Arguments = candidate_args;
return this;
}