Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

Resolving Symbolic References in a CodeDOM (Part 7)

, 2 Dec 2012 CDDL
Resolving symbolic references in a CodeDOM.
Nova.0.6.exe.zip
Mono.Cecil.dll
Nova.CLI.exe
Nova.CodeDOM.dll
Nova.Examples.exe
Nova.Studio.exe
Nova.Test.exe
Nova.UI.dll
Nova.0.6.zip
Nova.CLI
Properties
Nova.CodeDOM
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Interfaces
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Assemblies
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Parsing
Base
Properties
Rendering
Resolving
Utilities
Mono.Cecil
Reflection
Nova.Examples
Properties
Nova.Studio
Images
About.png
Configuration.png
EditCopy.png
EditCut.png
EditDelete.png
EditPaste.png
EditRedo.png
EditUndo.png
Error.png
Exit.png
FileNew.png
FileOpen.png
FileSave.png
FileSaveAll.png
FileSaveAs.png
Find.png
Help.png
Info.png
Logo.png
Options.png
Print.png
PrintPreview.png
Properties.png
Todo.png
Warning.png
Objects.ico
Properties
Settings.settings
Nova.Test
Properties
Nova.UI
CodeDOM
Annotations
Base
Comments
Base
DocComments
CodeRef
Base
List
Name
Base
Other
Simple
CompilerDirectives
Base
Conditionals
Base
Messages
Base
Pragmas
Base
Symbols
Base
Base
Expressions
AnonymousMethods
Base
Operators
Base
Binary
Arithmetic
Base
Assignment
Base
Bitwise
Base
Conditional
Relational
Base
Shift
Base
Other
Base
Unary
Base
Other
References
Base
GotoTargets
Base
Methods
Namespaces
Other
Properties
Types
Base
Variables
Base
Projects
Namespaces
References
Base
Statements
Base
Conditionals
Base
Exceptions
Generics
Constraints
Base
Iterators
Base
Jumps
Loops
Methods
OperatorDecls
Miscellaneous
Namespaces
Properties
Base
Events
Types
Base
Variables
Base
Properties
Resolving
Utilties
// The Nova Project by Ken Beckett.
// Copyright (C) 2007-2012 Inevitable Software, all rights reserved.
// Released under the Common Development and Distribution License, CDDL-1.0: http://opensource.org/licenses/cddl1.php

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Collections.Generic;

using Nova.CodeDOM;
using Nova.Utilities;
using Attribute = Nova.CodeDOM.Attribute;

namespace Nova.Resolving
{
    /// <summary>
    /// Used to resolve an <see cref="UnresolvedRef"/>.
    /// </summary>
    public class Resolver
    {
        #region /* STATIC FIELDS */

        /// <summary>
        /// Determines whether or not exact matching is enforced when only one possible match is found for an <see cref="UnresolvedRef"/>.
        /// By default, an exact match is required, but this can be set to false to allow a match if there is only one possibility.
        /// </summary>
        public static bool ExactMatching = true;

        /// <summary>
        /// The total number of resolve attempts for the current pass.
        /// </summary>
        public static int ResolveAttempts;

        /// <summary>
        /// The total number of resolve failures for the current pass.
        /// </summary>
        public static int ResolveFailures;

        #endregion

        #region /* FIELDS */

        /// <summary>
        /// The UnresolvedRef being resolved.
        /// </summary>
        protected UnresolvedRef _unresolvedRef;

        /// <summary>
        /// The targeted object category.
        /// </summary>
        /// <remarks>This field is basically a copy of the same field in the associated UnresolvedRef (for performance).</remarks>
        protected ResolveCategory _resolveCategory;

        /// <summary>
        /// Special flags controlling the symbol resolution.
        /// </summary>
        protected ResolveFlags _resolveFlags;

        /// <summary>
        /// The delegate used to find valid object types for the target category.
        /// </summary>
        protected Func<object, bool> _isValidCategory;

        /// <summary>
        /// The parent expression of the unresolved reference (if any).
        /// </summary>
        protected Expression _parentExpression;

        /// <summary>
        /// Indicates if a complete match exists.
        /// </summary>
        /// <remarks>This field is basically a copy of the same field in the associated UnresolvedRef (for performance).</remarks>
        protected bool _hasCompleteMatch;

        #endregion

        #region /* CONSTRUCTORS */

        /// <summary>
        /// Create a Resolver instance for the specified <see cref="UnresolvedRef"/>.
        /// </summary>
        /// <param name="unresolvedRef">The UnresolvedRef to be resolved.</param>
        /// <param name="resolveCategory">The ResolveCategory for the UnresolvedRef.</param>
        /// <param name="flags">Any ResolveFlags to be used.</param>
        public Resolver(UnresolvedRef unresolvedRef, ResolveCategory resolveCategory, ResolveFlags flags)
        {
            _unresolvedRef = unresolvedRef;
            _resolveCategory = resolveCategory;
            _resolveFlags = flags;
        }

        #endregion

        #region /* STATIC CONSTRUCTOR */

        static Resolver()
        {
            // Force a reference to CodeObject to trigger the loading of any config file if it hasn't been done yet
            CodeObject.ForceReference();
        }

        #endregion

        #region /* PROPERTIES */

        /// <summary>
        /// The <see cref="UnresolvedRef"/> being resolved.
        /// </summary>
        public UnresolvedRef UnresolvedRef
        {
            get { return _unresolvedRef; }
        }

        /// <summary>
        /// The <see cref="ResolveCategory"/> for the resolve attempt.
        /// </summary>
        public ResolveCategory ResolveCategory
        {
            get { return _resolveCategory; }
        }

        /// <summary>
        /// The <see cref="ResolveFlags"/> for the resolve attempt.
        /// </summary>
        public ResolveFlags ResolveFlags
        {
            get { return _resolveFlags; }
        }

        /// <summary>
        /// True if a complete match has been found.
        /// </summary>
        public bool HasCompleteMatch
        {
            get { return _hasCompleteMatch; }
        }

        /// <summary>
        /// True if we are looking for a method.
        /// </summary>
        public bool IsMethodCategory
        {
            get { return ResolveCategoryHelpers.IsMethod[(int)_resolveCategory]; }
        }

        /// <summary>
        /// The number of type arguments on the <see cref="UnresolvedRef"/> being resolved.
        /// </summary>
        public int TypeArgumentCount
        {
            get { return _unresolvedRef.TypeArgumentCount; }
        }

        /// <summary>
        /// Returns true if the reference being resolved has type arguments and the target category is for a type or constructor, otherwise false.
        /// </summary>
        public bool IsGenericTypeOrConstructor
        {
            get { return (_unresolvedRef.HasTypeArguments && ResolveCategoryHelpers.IsTypeOrConstructor[(int)_resolveCategory]); }
        }

        /// <summary>
        /// Returns true if the reference being resolved is in a DocCodeRefBase and has a Method category.
        /// </summary>
        public bool IsDocCodeRefToMethod
        {
            get { return (_resolveFlags.HasFlag(ResolveFlags.InDocCodeRef) && _resolveCategory == ResolveCategory.Method); }
        }

        #endregion

        #region /* METHODS */

        /// <summary>
        /// Attempt to resolve the associated UnresolvedRef object.
        /// </summary>
        /// <returns>The new reference object if resolved, otherwise the original UnresolvedRef.</returns>
        public SymbolicRef Resolve()
        {
            SymbolicRef resultRef;
            try
            {
                if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                    ++ResolveAttempts;

                string name = (string)_unresolvedRef.Reference;
                switch (_resolveCategory)
                {
                    case ResolveCategory.Type:
                        resultRef = ResolveSpecific(name, "Type name", "type names",
                            delegate(object obj) { return (obj is ITypeDecl && (!(obj is Alias) || ((Alias)obj).IsType)) || obj is TypeReference || obj is Type; });
                        break;
                    case ResolveCategory.Namespace:
                        resultRef = ResolveSpecific(name, "Namespace name", "namespace names", delegate(object obj) { return obj is Namespace || (obj is Alias && ((Alias)obj).IsNamespace); });
                        break;
                    case ResolveCategory.NamespaceOrType:
                        resultRef = ResolveSpecific(name, "Namespace or Type name", "namespace or type names",
                            delegate(object obj) { return obj is Namespace || obj is ITypeDecl || obj is TypeDefinition || obj is Type; });
                        break;
                    case ResolveCategory.Interface:
                        resultRef = ResolveSpecific(name, "Interface name", "interface names",
                            delegate(object obj) { return obj is InterfaceDecl || (obj is TypeDefinition && ((TypeDefinition)obj).IsInterface) || (obj is Type && ((Type)obj).IsInterface); });
                        break;
                    case ResolveCategory.Method:
                        resultRef = ResolveSpecific(name, "Method, delegate, or event name", "method, delegate, or event names",
                            delegate(object obj)
                                {
                                    return obj is MethodDecl || (obj is MethodDefinition && !((MethodDefinition)obj).IsConstructor) || obj is MethodInfo
                                            || (obj is IVariableDecl && HasPossibleDelegateType((IVariableDecl)obj))
                                            || (obj is FieldDefinition && IsDelegateType(((FieldDefinition)obj).FieldType))
                                            || (obj is PropertyDefinition && IsDelegateType(((PropertyDefinition)obj).PropertyType))
                                            || (obj is EventDefinition && IsDelegateType(((EventDefinition)obj).EventType))
                                            || (obj is FieldInfo && IsDelegateType(((FieldInfo)obj).FieldType))
                                            || (obj is PropertyInfo && IsDelegateType(((PropertyInfo)obj).PropertyType))
                                            || (obj is EventInfo && IsDelegateType(((EventInfo)obj).EventHandlerType))
                                            ||
                                            (IsDocCodeRefToMethod &&
                                            (obj is ConstructorDecl || (obj is MethodDefinition && ((MethodDefinition)obj).IsConstructor) || obj is ConstructorInfo));
                                });
                        break;
                    case ResolveCategory.Constructor:
                        resultRef = ResolveSpecific(name, "Constructor", "constructors",
                            delegate(object obj) { return obj is ConstructorDecl || (obj is MethodDefinition && ((MethodDefinition)obj).IsConstructor) || obj is ConstructorInfo; });
                        break;
                    case ResolveCategory.Attribute:
                        resultRef = ResolveAttribute("Attribute", "attributes",
                            delegate(object obj) { return obj is ConstructorDecl || (obj is MethodDefinition && ((MethodDefinition)obj).IsConstructor) || obj is ConstructorInfo; });
                        break;
                    case ResolveCategory.OperatorOverload:
                        resultRef = ResolveSpecific(name, "Overloaded operator", "overloaded operators",
                            delegate(object obj) { return obj is OperatorDecl || obj is MethodDefinition || obj is MethodInfo; });
                        break;
                    case ResolveCategory.Property:
                        resultRef = ResolveSpecific(name, "Property name", "property names",
                            delegate(object obj) { return obj is PropertyDecl || (obj is PropertyDefinition && !((PropertyDefinition)obj).HasParameters)
                                || (obj is PropertyInfo && !PropertyInfoUtil.IsIndexed((PropertyInfo)obj)); });
                        break;
                    case ResolveCategory.Indexer:
                        resultRef = ResolveSpecific(name, "Indexer", "indexers",
                            delegate(object obj) { return obj is IndexerDecl || (obj is PropertyDefinition && ((PropertyDefinition)obj).HasParameters) ||
                                (obj is PropertyInfo && PropertyInfoUtil.IsIndexed((PropertyInfo)obj)); });
                        break;
                    case ResolveCategory.Event:
                        resultRef = ResolveSpecific(name, "Event name", "event names",
                            delegate(object obj) { return obj is EventDecl || obj is EventDefinition || obj is EventInfo
                                || (obj is FieldDecl && ((FieldDecl)obj).IsEvent); });
                        break;
                    case ResolveCategory.TypeParameter:
                    case ResolveCategory.LocalTypeParameter:
                        resultRef = ResolveSpecific(name, "Type parameter name", "type parameter names", delegate(object obj) { return obj is TypeParameter || obj is GenericParameter; });
                        break;
                    case ResolveCategory.Parameter:
                        resultRef = ResolveSpecific(name, "Parameter name", "parameter names",
                            delegate(object obj) { return obj is ParameterDecl || obj is ParameterDefinition || obj is ParameterInfo; });
                        break;
                    case ResolveCategory.GotoTarget:
                        resultRef = ResolveSpecific(name, "Label or case name", "label or case names", delegate(object obj) { return obj is Label || obj is SwitchItem; });
                        break;
                    case ResolveCategory.NamespaceAlias:
                        resultRef = ResolveSpecific(name, "Namespace alias name", "namespace alias names",
                            delegate(object obj) { return obj is ExternAlias || (obj is Alias && ((Alias)obj).IsNamespace); });
                        break;
                    case ResolveCategory.RootNamespace:
                        resultRef = ResolveSpecific(name, "Root namespace name", "root namespace names", delegate(object obj) { return obj is Namespace; });
                        break;
                    case ResolveCategory.Expression:
                        // Handle references in expressions - the exact category isn't known, but we can still filter out illegal
                        // object types.  For example, constructors will be filtered out, so that only the parent types will be matched.
                        // Destructors can also be eliminated, since they can't be referenced directly.  Indexers aren't normally referenced
                        // directly, but this can be done in doc comments, so they're legal, but only if we have an UnresolvedThisRef.
                        // Alias objects (Namespace or Type aliases) are valid in expressions, because they can be prefixed on a reference
                        // to a static member of a type, or on an enum member.  Ditto for ExternAlias.
                        resultRef = ResolveUnspecified(delegate(object obj)
                            {
                                return !(obj is ConstructorDecl || (obj is MethodDefinition && ((MethodDefinition)obj).IsConstructor)
                                         || obj is ConstructorInfo || obj is DestructorDecl)
                                       && (!(obj is IndexerDecl || (obj is PropertyDefinition && ((PropertyDefinition)obj).HasParameters)
                                             || (obj is PropertyInfo && PropertyInfoUtil.IsIndexed((PropertyInfo)obj))) || _unresolvedRef is UnresolvedThisRef);
                            });
                        break;
                    default:  // ResolveCategory.CodeObject, ResolveCategory.Unspecified
                        // These other categories are placeholders - this routine should never be called with them, or something
                        // is wrong somewhere in the code.  Don't resolve the reference, so that the problem will show up.
                        resultRef = _unresolvedRef;
                        break;
                }

                if (resultRef == null || resultRef is UnresolvedRef)
                {
                    // Count resolve failures, but ignore if "quiet" mode
                    if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                        ++ResolveFailures;
                }
                else
                {
                    // Go ahead and count successful resolves in "quiet" mode
                    if (_resolveFlags.HasFlag(ResolveFlags.Quiet))
                        ++ResolveAttempts;
                }

                // If we're about to replace this object, dispose it now
                if (_unresolvedRef != resultRef)
                    _unresolvedRef.Dispose();
            }
            catch (Exception ex)
            {
                string message = Log.Exception(ex, "resolving" + (_unresolvedRef != null ? " '" + _unresolvedRef.Name + "'" : ""));
                if (_unresolvedRef != null)
                {
                    _unresolvedRef.AttachMessage(message, _resolveFlags.HasFlag(ResolveFlags.InDocComment)
                        ? MessageSeverity.Warning : MessageSeverity.Error, MessageSource.Resolve);
                }
                resultRef = _unresolvedRef;
            }

            return resultRef;
        }

        protected bool HasPossibleDelegateType(IVariableDecl variableDecl)
        {
            Expression type = variableDecl.Type;
            if (type != null)
            {
                TypeRefBase typeRefBase = type.EvaluateType();
                if (typeRefBase != null)
                {
                    typeRefBase = typeRefBase.EvaluateTypeArgumentTypes(_unresolvedRef.Parent, _unresolvedRef);
                    return typeRefBase.IsPossibleDelegateType;
                }
            }
            return true;
        }

        protected bool IsDelegateType(TypeReference typeReference)
        {
            return TypeRef.Create(typeReference).EvaluateTypeArgumentTypes(_unresolvedRef.Parent, _unresolvedRef).IsDelegateType;
        }

        protected bool IsDelegateType(Type type)
        {
            return TypeRef.Create(type).EvaluateTypeArgumentTypes(_unresolvedRef.Parent, _unresolvedRef).IsDelegateType;
        }

        /// <summary>
        /// Resolve the reference using a known category.
        /// </summary>
        protected SymbolicRef ResolveSpecific(string name, string expected, string noMatches, Func<object, bool> isValidCategory)
        {
            // Matching type and method arguments can be either 'loose' or exact - this is controlled by
            // the ExactMatching constant.  Exact matching can be turned on, but the general idea is to
            // match anyway if the arguments don't match, and then have the analysis phase indicate an
            // error if the arguments don't match exactly.
            // If a match can't be determined because of ambiguity, the unresolved symbol is updated
            // to indicate the appropriate error message and list of possible matches.

            SymbolicRef resultRef = _unresolvedRef;
            CodeObject parent = _unresolvedRef.Parent;
            _isValidCategory = isValidCategory;

            // The parent type is a SymbolicRef because it might be a NamespaceRef
            SymbolicRef parentTypeRef = null;

            // Special handling for operator overloads
            if (_resolveCategory == ResolveCategory.OperatorOverload)
            {
                if (parent is BinaryOperator && parent.HiddenRef == _unresolvedRef)
                {
                    // Evaluate a possible binary operator-overload reference - start by determining the types of the
                    // two operands.  Abort if they're not TypeRefs.  For now, we're ignoring TypeParameterRefs - they
                    // will fail to resolve, but that's OK since any operator overload should only be done with actual
                    // types, which means it would occur in the generated code (not even sure how this is handled).
                    // Note that 'user-defined' overloaded operators can appear even in built-in types such as 'System.Decimal'.
                    BinaryOperator binaryOperator = (BinaryOperator)parent;
                    Expression left = binaryOperator.Left;
                    Expression right = binaryOperator.Right;
                    if (left == null || right == null)
                        return null;
                    TypeRefBase leftTypeRefBase = left.EvaluateType(true);
                    TypeRefBase rightTypeRefBase = right.EvaluateType(true);

                    // If the left and right are the same type, only lookup on one of them
                    if (leftTypeRefBase != null && leftTypeRefBase.IsSameRef(rightTypeRefBase))
                        leftTypeRefBase.ResolveRef(name, this);
                    else
                    {
                        // Only do lookups on built-in types if no operators exist on user types
                        ResolveBinaryOperators(leftTypeRefBase, rightTypeRefBase, name, false);
                        if (_unresolvedRef.Matches == null || _unresolvedRef.Matches.Count == 0)
                            ResolveBinaryOperators(leftTypeRefBase, rightTypeRefBase, name, true);
                    }
                }
                else if (parent is UnaryOperator && parent.HiddenRef == _unresolvedRef)
                {
                    // Evaluate a possible unary operator-overload reference - start by determining the type of the operand.
                    // Note that 'user-defined' overloaded operators can appear even in built-in types such as 'System.Decimal'.
                    _parentExpression = ((UnaryOperator)parent).Expression;
                    TypeRef typeRef = _parentExpression.EvaluateType(true) as TypeRef;

                    // Abort if it's not a TypeRef.  For now, we're ignoring TypeParameterRefs.
                    if (typeRef == null)
                        return null;

                    // Abort if it's a cast to an 'object' or the same type as the expression
                    if (_unresolvedRef.Name == "op_Explicit" && _unresolvedRef.Parent is Cast)
                    {
                        TypeRefBase castTypeRef = ((Cast)_unresolvedRef.Parent).Type.EvaluateType();
                        if (castTypeRef.IsSameRef(TypeRef.ObjectRef) || castTypeRef.IsSameRef(typeRef))
                            return null;
                    }

                    typeRef.ResolveRef(name, this);
                }
                else
                    return null;  // Abort if not recognized as a valid operator overload
            }
            else
            {
                if (parent is Dot && ((Dot)parent).Right == _unresolvedRef)
                {
                    // Resolve the symbol as a member of the parent object
                    ResolveMember(ref parentTypeRef, name);
                }
                else if (parent is Lookup && ((Lookup)parent).Right == _unresolvedRef)
                {
                    // Handle symbols on the right side of a '::' - restrict the search scope to the namespace alias on the left
                    Expression leftExpression = ((Lookup)parent).Left;
                    Namespace rootNamespace = null;
                    if (leftExpression is ExternAliasRef)
                        rootNamespace = ((ExternAliasRef)leftExpression).RootNamespace;
                    else if (leftExpression is AliasRef)
                        rootNamespace = ((AliasRef)leftExpression).Namespace.Namespace;
                    if (rootNamespace != null)
                        rootNamespace.ResolveRef(name, this, false);
                }
                else if (parent is NewObject && parent.HiddenRef == _unresolvedRef)
                {
                    // Handle special hidden ConstructorRef references for NewObjects
                    _parentExpression = ((NewObject)parent).Expression;
                    parentTypeRef = _parentExpression.EvaluateType();
                    if (parentTypeRef != null)
                        AddMatchInternal(parentTypeRef.Reference, true);
                }
                else if (parent is Index && parent.HiddenRef == _unresolvedRef)
                {
                    // Handle special hidden IndexerRef references for Index operators
                    _parentExpression = ((Index)parent).Expression;
                    parentTypeRef = _parentExpression.EvaluateType();
                    if (parentTypeRef != null)
                        parentTypeRef.ResolveIndexerRef(this);
                }
                else if (parent is ConstructorInitializer && parent.HiddenRef == _unresolvedRef)
                {
                    // Handle constructor initializer calls (': this(...)' or ': base(...)')
                    if (parent is ThisInitializer)
                    {
                        // For ': this(...)', always evaluate as a constructor of the current type
                        AddMatchInternal(_unresolvedRef.FindParent<TypeDecl>(), true);
                    }
                    else
                    {
                        // For ': base(...)', always evaluate as a constructor of the immediate base type
                        TypeDecl typeDecl = _unresolvedRef.FindParent<TypeDecl>();
                        if (typeDecl is ClassDecl)
                        {
                            TypeRef baseRef = typeDecl.GetBaseType();
                            AddMatchInternal(baseRef.Reference, true);
                        }
                    }
                }
                else if (parent != null)
                {
                    // Search up the code tree, trying to resolve the symbol.  Multiple matches might be found, and
                    // partial matches might also be found.  The search stops at the current scope if at least one
                    // match is found, and will also stop at certain points for certain search categories.  If a
                    // match is found, any partial matches will be ignored.  If multiple matches are found, we will
                    // attempt to trim the list, getting it down to a single match if possible.  If no match is found,
                    // any list of partial matches will also be trimmed if possible.
                    if (_resolveCategory == ResolveCategory.GotoTarget)
                        parent.ResolveGotoTargetUp(name, this);
                    else
                        parent.ResolveRefUp(name, this);
                }
            }

            // If we have multiple matches, try filtering them (by better method matches, or other parts of partial types)
            if (_unresolvedRef.Matches != null && _unresolvedRef.Matches.Count > 1)
                FilterMatches(parentTypeRef as TypeRefBase);
            // If we're in a documentation comment, try ignoring accessibility rules to get a better match
            if (_unresolvedRef.Matches != null && _unresolvedRef.Matches.Count > 0 && _resolveFlags.HasFlag(ResolveFlags.InDocComment))
                FilterToInaccessibleOnly();

            MatchCandidates matches = _unresolvedRef.Matches;
            int foundCount = (matches != null ? matches.Count : 0);
            if (_resolveCategory == ResolveCategory.OperatorOverload)
            {
                // If it's an operator overload and we didn't find a single complete match, ignore it (use the default operator)
                //if (foundCount < 1 || !matches.IsCompleteMatch)  // Use this to see multiple complete matches for debugging
                if (foundCount != 1 || !matches.IsCompleteMatch)
                    return null;
                _resolveFlags &= ~ResolveFlags.Quiet;  // Turn on messages at this point (really only applies for debugging logic above)
            }

            string message = null;
            MessageSeverity messageType = _resolveFlags.HasFlag(ResolveFlags.InDocComment) ? MessageSeverity.Warning : MessageSeverity.Error;
            if (foundCount == 1)
            {
                // Generate the reference or error message, as appropriate
                MatchCandidate finalMatch = matches[0];
                if (finalMatch.IsCompleteMatch || (!ExactMatching && finalMatch.IsCategoryMatch))
                {
                    resultRef = _unresolvedRef.CreateRef(finalMatch.Object, true);

                    // If we have inferred type arguments, copy them to the result (this can occur
                    // for MethodRefs or TypeRefs in doc comments).
                    if (finalMatch.InferredTypeArguments != null)
                        finalMatch.CopyInferredTypeArguments((TypeRefBase)resultRef);
                }
                else if (finalMatch.IsCategoryMatch)
                {
                    if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                    {
                        message = expected + " found, but " + finalMatch.GetMismatchDescription();
                        if (IsErrorDueToUnresolvedOnly(parentTypeRef))
                            messageType = MessageSeverity.Warning;
                    }
                }
                else if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                    message = expected + " expected, but " + GetWrongCategoryError(finalMatch.Object) + " found";
            }
            else if (foundCount > 1)
            {
                if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                {
                    // Generate the appropriate error message
                    if (matches.IsCompleteMatch)
                        message = expected + " exists, but multiple matches were found";
                    else if (matches.IsCategoryMatch)
                    {
                        message = expected + " exists, but multiple incomplete matches were found";
                        if (IsErrorDueToUnresolvedOnly(parentTypeRef))
                            messageType = MessageSeverity.Warning;
                    }
                    else
                        message = expected + " expected, but multiple matches were found, and none of them are " + noMatches;
                }
            }
            else if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
            {
                if (_parentExpression != null)
                {
                    // Set an appropriate error message based on the parent (if any)
                    if (_parentExpression is UnresolvedRef)
                    {
                        message = "Parent is unresolved";
                        messageType = MessageSeverity.Warning;
                    }
                    else if (_parentExpression is AliasRef && _parentExpression.HasUnresolvedRef())
                    {
                        message = "Parent is an unresolved alias";
                        messageType = MessageSeverity.Warning;
                    }
                    else if ((parentTypeRef is TypeRefBase && parentTypeRef.HasUnresolvedRef()) || (parentTypeRef == null))
                    {
                        message = (_parentExpression is SymbolicRef ? "Type of parent object is undetermined" : "Type of parent expression is undetermined");
                        messageType = MessageSeverity.Warning;
                    }
                    else
                    {
                        message = expected + " expected, but " + parentTypeRef.Category + " '" + parentTypeRef.GetDescription() + "' has no member '" + name + "'";
                        if (parentTypeRef is TypeRefBase && parentTypeRef.HasUnresolvedRef())
                            messageType = MessageSeverity.Warning;
                    }
                }
                else
                    message = expected + " expected, but symbol '" + name + "' can't be resolved";
            }

            if (message != null)
                _unresolvedRef.AttachMessage(message, messageType, MessageSource.Resolve);

            return resultRef;
        }

        protected void ResolveBinaryOperators(TypeRefBase leftTypeRefBase, TypeRefBase rightTypeRefBase, string name, bool checkBuiltInTypes)
        {
            // Only lookup on the left operand if it's not a base type of the right operand (to avoid dups)
            if (leftTypeRefBase is TypeRef)
            {
                if (leftTypeRefBase.IsBuiltInType == checkBuiltInTypes && !(rightTypeRefBase is TypeRef && ((TypeRef)rightTypeRefBase).IsSubclassOf((TypeRef)leftTypeRefBase)))
                    leftTypeRefBase.ResolveRef(name, this);
            }

            // Only lookup on the right operand if it's not a base type of the left operand (to avoid dups)
            if (rightTypeRefBase is TypeRef)
            {
                if (rightTypeRefBase.IsBuiltInType == checkBuiltInTypes && !(leftTypeRefBase is TypeRef && ((TypeRef)leftTypeRefBase).IsSubclassOf((TypeRef)rightTypeRefBase)))
                    rightTypeRefBase.ResolveRef(name, this);
            }
        }

        protected static string GetWrongCategoryError(object obj)
        {
            string error;
            if (obj is INamedCodeObject)
            {
                INamedCodeObject namedObj = (INamedCodeObject)obj;
                error = namedObj.Category + " name '" + namedObj.Name + "'";
            }
            else if (obj is MemberReference)
            {
                MemberReference memberDefinition = (MemberReference)obj;
                error = MemberReferenceUtil.GetCategory(memberDefinition) + " name '" + memberDefinition.Name + "'";
            }
            else if (obj is ParameterDefinition)
            {
                ParameterDefinition parameterDefinition = (ParameterDefinition)obj;
                error = ParameterDefinitionUtil.GetCategory(parameterDefinition) + " name '" + parameterDefinition.Name + "'";
            }
            else if (obj is MemberInfo)
            {
                MemberInfo memberInfo = (MemberInfo)obj;
                error = MemberInfoUtil.GetCategory(memberInfo) + " name '" + memberInfo.Name + "'";
            }
            else if (obj is ParameterInfo)
            {
                ParameterInfo parameterInfo = (ParameterInfo)obj;
                error = ParameterInfoUtil.GetCategory(parameterInfo) + " name '" + parameterInfo.Name + "'";
            }
            else
                error = "unknown object";
            return error;
        }

        protected bool IsErrorDueToUnresolvedOnly(SymbolicRef parentTypeRef)
        {
            return ((parentTypeRef is TypeRefBase && parentTypeRef.HasUnresolvedRef()) || _unresolvedRef.IsAnyMismatchDueToUnresolvedOnly());
        }

        /// <summary>
        /// Resolve the reference as an attribute.
        /// </summary>
        protected SymbolicRef ResolveAttribute(string expected, string noMatches, Func<object, bool> isValidCategory)
        {
            // First try adding an "Attribute" suffix, regardless of whether or not it already has one - this is
            // necessary to match names such as "XmlAttributeAttribute".
            string name = (string)_unresolvedRef.Reference;
            SymbolicRef resultRef = ResolveSpecific(name + Attribute.NameSuffix, expected, noMatches, isValidCategory);
            if (resultRef is UnresolvedRef && !((UnresolvedRef)resultRef).HasMatches)
            {
                // If that didn't find any matches, try the unmodified name
                _unresolvedRef.ResetResolutionMembers();
                resultRef = ResolveSpecific(name, expected, noMatches, isValidCategory);
            }
            return resultRef;
        }

        /// <summary>
        /// This method is called to resolve a symbol in the situation where a category couldn't be determined.
        /// Because parsing can always determine the category in various situations, this method should never be
        /// called for: Generic Types, Namespaces, Methods (with parens), Constructors, Attributes, Goto Targets,
        /// Directive Expressions, literal constants, Indexers, Events, variables of delegate type.
        /// So, it doesn't need to worry about type arguments or method arguments.
        /// This leaves the possibilities of: Non-Generic Types, Type Parameters, Properties, Fields, Locals,
        /// Parameters, and non-invoked Method names (without parens, such as when assigned to delegates or passed
        /// as a parameter of delegate type).  Namespace prefixes can also exist for many of these.
        /// </summary>
        protected SymbolicRef ResolveUnspecified(Func<object, bool> isValidCategory)
        {
            SymbolicRef resultRef = _unresolvedRef;
            string name = (string)_unresolvedRef.Reference;
            CodeObject parent = _unresolvedRef.Parent;
            _isValidCategory = isValidCategory;

            // The parent type is a SymbolicRef because it might be a NamespaceRef
            SymbolicRef parentTypeRef = null;

            if (parent is Dot && ((Dot)parent).Right == _unresolvedRef)
            {
                // Resolve the symbol as a member of the parent object
                ResolveMember(ref parentTypeRef, name);
            }
            else if (parent != null)
            {
                if (parent.GetType() == typeof(Assignment) && ((Assignment)parent).Left == _unresolvedRef)
                {
                    CodeObject grandParent = parent.Parent;

                    // The symbol is the 'lvalue' of an assignment - check some special cases
                    if (grandParent is Initializer)
                    {
                        // Handle assignment lvalues in object initializers - restrict the search scope to the great-
                        // grandparent if it's a NewObject, Initializer, or Assignment (ignore NewArray).
                        CodeObject greatGrandParent = grandParent.Parent;
                        if (greatGrandParent is Expression && !(greatGrandParent is NewArray))
                            parentTypeRef = ((Expression)greatGrandParent).EvaluateType();
                    }
                    else if (grandParent is Call && grandParent.Parent is Attribute)
                    {
                        // Handle assignment lvalues in attribute calls - restrict the search scope to the parent type
                        Call call = (Call)grandParent;
                        parentTypeRef = call.EvaluateType();

                        // Parameters are resolved first to allow the call expression to be resolved, so we
                        // probably have to resolve the call expression at this point (which should work now,
                        // as all of the 'real' parameters should be resolved now).
                        if (parentTypeRef is UnresolvedRef)
                        {
                            call.Expression = (Expression)call.Expression.Resolve(ResolveCategory.Attribute, _resolveFlags);
                            parentTypeRef = call.EvaluateType();
                        }
                    }
                }

                // If we have a parent type, search it for the member, otherwise search up the code tree
                if (parentTypeRef != null)
                    parentTypeRef.ResolveRef(name, this);
                else
                    parent.ResolveRefUp(name, this);
            }

            // If we have multiple matches, try filtering them (by better method matches, or other parts of partial types)
            if (_unresolvedRef.Matches != null && _unresolvedRef.Matches.Count > 1)
                FilterMatches(null);
            // If we're in a documentation comment, try ignoring accessibility rules to get a better match
            if (_unresolvedRef.Matches != null && _unresolvedRef.Matches.Count > 0 && _resolveFlags.HasFlag(ResolveFlags.InDocComment))
                FilterToInaccessibleOnly();

            string message = null;
            MessageSeverity messageType = _resolveFlags.HasFlag(ResolveFlags.InDocComment) ? MessageSeverity.Warning : MessageSeverity.Error;

            MatchCandidates matches = _unresolvedRef.Matches;
            int foundCount = (matches != null ? matches.Count : 0);
            if (foundCount == 1)
            {
                // Generate the reference or error message, as appropriate
                MatchCandidate finalMatch = matches[0];
                if (finalMatch.IsCompleteMatch || (!ExactMatching && finalMatch.IsCategoryMatch))
                {
                    resultRef = _unresolvedRef.CreateRef(finalMatch.Object, true);

                    // If we have inferred type arguments, copy them to the result (this can occur
                    // for MethodRefs or TypeRefs in doc comments).
                    if (finalMatch.InferredTypeArguments != null)
                        finalMatch.CopyInferredTypeArguments((TypeRefBase)resultRef);
                }
                else if (finalMatch.IsCategoryMatch)
                {
                    if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                    {
                        message = "Match found, but " + finalMatch.GetMismatchDescription();
                        if (IsErrorDueToUnresolvedOnly(parentTypeRef))
                            messageType = MessageSeverity.Warning;
                    }
                }
                else if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                    message = "Match found, but it isn't a valid object type in this context";
            }
            else if (foundCount > 1)
            {
                if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
                {
                    // Generate the appropriate error message
                    if (matches.IsCompleteMatch)
                        message = "Multiple matches were found";
                    else if (matches.IsCategoryMatch)
                    {
                        message = "Multiple incomplete matches were found";
                        if (IsErrorDueToUnresolvedOnly(parentTypeRef))
                            messageType = MessageSeverity.Warning;
                    }
                    else
                        message = "Multiple matches were found, but they are not valid object types in this context";
                }
            }
            else if (!_resolveFlags.HasFlag(ResolveFlags.Quiet))
            {
                if (_parentExpression != null)
                {
                    // Set an appropriate error message based on the parent (if any)
                    if (_parentExpression is UnresolvedRef)
                    {
                        message = "Parent is unresolved";
                        messageType = MessageSeverity.Warning;
                    }
                    else if (_parentExpression is AliasRef && _parentExpression.HasUnresolvedRef())
                    {
                        message = "Parent is an unresolved alias";
                        messageType = MessageSeverity.Warning;
                    }
                    else if ((parentTypeRef is TypeRefBase && parentTypeRef.HasUnresolvedRef()) || (parentTypeRef == null))
                    {
                        message = (_parentExpression is SymbolicRef ? "Type of parent object is undetermined" : "Type of parent expression is undetermined");
                        messageType = MessageSeverity.Warning;
                    }
                    else
                    {
                        message = "The " + parentTypeRef.Category + " '" + parentTypeRef.GetDescription() + "' has no member '" + name + "'";
                        if (parentTypeRef is TypeRefBase && parentTypeRef.HasUnresolvedRef())
                            messageType = MessageSeverity.Warning;
                    }
                }
                else
                    message = "Symbol '" + name + "' " + (name.Length == 1 && (char.IsPunctuation(name, 0) || char.IsSymbol(name, 0)) ? "unexpected" : "can't be resolved");
            }

            if (message != null)
                _unresolvedRef.AttachMessage(message, messageType, MessageSource.Resolve);

            return resultRef;
        }

        protected void ResolveMember(ref SymbolicRef parentTypeRef, string name)
        {
            CodeObject parent = _unresolvedRef.Parent;

            // Handle symbols on the right side of a '.' - restrict the search scope to the parent object type
            _parentExpression = ((Dot)parent).Left;
            if (_parentExpression == null)
                return;

            // If the parent expression is an UnresolvedRef because it's an ambiguous type reference, try to
            // resolve as a member of each of the possible types in order to avoid cascading errors.
            if (_parentExpression is UnresolvedRef)
            {
                UnresolvedRef parentUnresolvedRef = (UnresolvedRef)_parentExpression;
                if (parentUnresolvedRef.HasMatches)
                {
                    foreach (MatchCandidate candidate in parentUnresolvedRef.Matches)
                    {
                        object candidateObject = candidate.Object;
                        if (candidateObject is ITypeDecl || candidateObject is TypeDefinition || candidateObject is Type)
                        {
                            // Try to resolve as a member of the type, and exit if it works
                            TypeRefBase currentRef = (TypeRefBase)_unresolvedRef.CreateRef(candidateObject, true);
                            _parentExpression = currentRef;  // Override the parent expression
                            currentRef.ResolveRef(name, this);
                            if (_unresolvedRef.HasCompleteMatch)
                                return;
                        }
                    }

                    // Reset and restore the parent expression if it didn't work
                    _unresolvedRef.ResetResolutionMembers();
                    _parentExpression = ((Dot)parent).Left;
                }
                return;
            }

            // In this case, the parent expression might evaluate to a NamespaceRef (in addition to a TypeRef or UnresolvedRef)
            parentTypeRef = _parentExpression.EvaluateTypeOrNamespace();
            if (parentTypeRef != null)
            {
                // Search the parent Type or Namespace for the child member
                parentTypeRef.ResolveRef(name, this);

                // If the member failed to resolve, and the parent is a simple name of a variable (or property), then check if a type
                // exists in scope with the same name as the parent ref, and if so, then try resolving as a *static* member of that type,
                // and if successful, convert the parent expression into a TypeRef of that type.  This allows what would otherwise be a
                // compile error to be successfully resolved.  Note that the spec says that the evaluated type of the variable should match
                // the name of the variable, but this is actually WRONG - although this is often true, it's possible for the type to be a
                // base type or interface of a valid type that matches the variable name. For this reason, and to avoid a string compare of
                // the type name, it seems best to only check for this situation after the initial resolve fails, rather than trying to
                // detect it in advance.
                if (!_unresolvedRef.HasCompleteMatch && _parentExpression is VariableRef)
                {
                    // Determine if the parent expression could be alternatively interpreted as a type
                    UnresolvedRef unresolvedRef = new UnresolvedRef((VariableRef)_parentExpression, ResolveCategory.Type);
                    TypeRef typeRef = unresolvedRef.Resolve(ResolveCategory.Type, ResolveFlags.Quiet) as TypeRef;
                    if (typeRef != null)
                    {
                        // If so, determine if this unresolved ref is a valid static member of that type
                        _parentExpression = typeRef;
                        typeRef.ResolveRef(name, this);
                        if (_unresolvedRef.HasCompleteMatch)
                        {
                            // If so, replace the parent VariableRef with the TypeRef
                            ((Dot)parent).Left = typeRef;
                        }
                        else
                        {
                            // Reset and restore the parent expression if it didn't work, and re-resolve
                            // to restore the exact state.
                            _unresolvedRef.ResetResolutionMembers();
                            _parentExpression = ((Dot)parent).Left;
                            parentTypeRef.ResolveRef(name, this);
                        }
                    }
                    else
                        unresolvedRef.Dispose();
                }
            }
        }

        #region /* FILTER MATCHES RELATED */

        protected void FilterMatches(TypeRefBase parentTypeRef)
        {
            // Don't bother filtering non-complete matches, because we'll want to see all of the possible
            // options in the error message.  Also, in the case of matching a method group passed as a
            // parameter (see IsImplicitlyConvertibleTo in this class), we might match one of the other
            // members later, so we need to keep them all around.
            if (_unresolvedRef.HasCompleteMatch)
            {
                MatchCandidates matches = _unresolvedRef.Matches;

                // If we have multiple complete method matches, try filtering for better matches based upon the
                // parameters, or in the case of user-defined explicit conversions, the most-specific conversion.
                // If that doesn't work, we'll also try filtering out other parts of partial types.
                bool isMethodCategory = ResolveCategoryHelpers.IsMethod[(int)_resolveCategory];
                object firstMatch = matches[0].Object;
                if (isMethodCategory || firstMatch is MethodDecl || firstMatch is MethodDefinition || firstMatch is MethodInfo)
                {
                    bool hasUnresolvedDelegateTypes;
                    List<Expression> methodArguments = GetParentArguments(isMethodCategory, out hasUnresolvedDelegateTypes);
                    if (methodArguments != null)
                    {
                        // Check for multiple matching user-defined explicit conversion operators
                        if (_resolveCategory == ResolveCategory.OperatorOverload && _unresolvedRef.Name == "op_Explicit" && _unresolvedRef.Parent is Cast)
                        {
                            // If we can determine a single best conversion, use it (otherwise, no filtering)
                            MatchCandidate bestConversion = FindBestExplicitConversion(matches, isMethodCategory, methodArguments);
                            if (bestConversion != null)
                            {
                                matches = matches.New();
                                matches.Add(bestConversion);
                            }
                        }
                        else
                        {
                            // If we have multiple complete method matches, try looking for better vs lessor method matches
                            // based upon the matching of their parameters (ignore if non-method objects have been matched).
                            // Unlike the spec, we allow ref/out mismatches (although the type must match exactly) in AddMatch(),
                            // relying on the analysis phase to flag them as errors.  Since it's possible to overload methods
                            // with only ref/out differences, we could get dups, and also need to filter them here.
                            MatchCandidates betterMatches = matches.New();
                            betterMatches.Add(matches[0]);
                            for (int i = 1; i < matches.Count; ++i)
                            {
                                MatchCandidate candidate = matches[i];
                                object obj = candidate.Object;
                                object betterMethod = null;
                                if (isMethodCategory || obj is MethodDecl || obj is MethodDefinition || obj is MethodInfo)
                                    betterMethod = FindBetterMethod(betterMatches[0], candidate, methodArguments, _unresolvedRef);
                                if (betterMethod == null)
                                {
                                    // Neither method is better, so add to the list of similar matches
                                    betterMatches.Add(candidate);
                                }
                                else if (betterMethod == obj)
                                {
                                    // Replace any existing better matches with the new better match
                                    betterMatches.Clear();
                                    betterMatches.Add(candidate);
                                }
                            }
                            matches = betterMatches;
                        }
                    }
                }

                // Try filtering out extra parts of partial types (just use the first one)
                if (matches.Count > 1)
                {
                    MatchCandidates partialMatches = matches.New();
                    bool isFirstPartial = true;
                    foreach (MatchCandidate result in matches)
                    {
                        if (result.Object is TypeDecl && ((TypeDecl)result.Object).IsPartial)
                        {
                            // Only keep the first part of partial types
                            if (isFirstPartial)
                            {
                                partialMatches.Add(result);
                                isFirstPartial = false;
                            }
                        }
                        else
                            partialMatches.Add(result);
                    }

                    // Only keep the results if we found at least one match
                    if (partialMatches.Count > 0)
                        matches = partialMatches;
                }

                // Try filtering out hidden members.
                // Other logic (such as not searching base classes when a complete match is found in a derived class,
                // or checking the categories of matches) will prevent duplicates here.  However, in the case of multiple
                // inheritance of interfaces (which can also occur as generic constraints), it's possible to have matches
                // that should be hidden.
                if (matches.Count > 1)
                {
                    MatchCandidates nonHiddenMatches = matches.New();
                    // First, determine the declaring types of any methods, properties, indexers, and events
                    TypeRefBase[] declaringTypeRefs = new TypeRefBase[matches.Count];
                    for (int i = 0; i < matches.Count; ++i)
                    {
                        MatchCandidate candidate = matches[i];
                        object obj = candidate.Object;
                        if (obj is MethodDecl || obj is MethodDefinition || obj is MethodInfo)
                            declaringTypeRefs[i] = MethodRef.GetDeclaringType(obj);
                        else if (obj is EventDecl || obj is EventDefinition || obj is EventInfo)
                            declaringTypeRefs[i] = PropertyRef.GetDeclaringType(obj);
                        else if (obj is PropertyDeclBase || obj is PropertyDefinition || obj is PropertyInfo)
                            declaringTypeRefs[i] = PropertyRef.GetDeclaringType(obj);
                        else
                            declaringTypeRefs[i] = null;
                    }
                    // Now, for each candidate, hide it if there's another match in a 'derived' interface (don't bother
                    // checking method parameters, because we're only dealing with complete matches here).
                    for (int i = 0; i < matches.Count; ++i)
                    {
                        bool isHidden = false;
                        TypeRefBase declaringType = declaringTypeRefs[i];
                        if (declaringType != null && declaringType.IsInterface)
                        {
                            for (int j = 0; j < matches.Count; ++j)
                            {
                                if (j != i)
                                {
                                    TypeRefBase otherType = declaringTypeRefs[j];
                                    if (otherType != null && otherType.IsInterface && ((TypeRef)otherType).IsImplementationOf((TypeRef)declaringType))
                                    {
                                        isHidden = true;
                                        break;
                                    }
                                }
                            }
                        }
                        if (!isHidden)
                            nonHiddenMatches.Add(matches[i]);
                    }

                    // Only keep the results if we found at least one match
                    if (nonHiddenMatches.Count > 0)
                        matches = nonHiddenMatches;
                }

                _unresolvedRef.Matches = matches;
            }
        }

        protected void FilterToInaccessibleOnly()
        {
            // Try filtering to inaccessible matches only, and if it results in a single match, force it to be
            // accessible (this routine is used only when in documentation comments).
            MatchCandidates matches = _unresolvedRef.Matches;
            MatchCandidates inaccessibleMatches = matches.New();
            foreach (MatchCandidate result in matches)
            {
                if (result.IsMismatchDueToAccessibilityOnly())
                    inaccessibleMatches.Add(result);
            }

            // Only keep the results if we found at least one match
            if (inaccessibleMatches.Count > 0)
                matches = inaccessibleMatches;
            // Furthermore, force a single match to fake being accessible
            if (matches.Count == 1)
                matches[0].IsAccessible = true;

            _unresolvedRef.Matches = matches;
        }


        /// <summary>
        /// We have multiple matching user-defined explicit conversion operators - try to find a single best conversion.
        /// </summary>
        /// <returns>The single best conversion if found, otherwise null.</returns>
        protected MatchCandidate FindBestExplicitConversion(MatchCandidates matches, bool isMethodCategory, List<Expression> methodArguments)
        {
            MatchCandidate bestConversion = null;
            TypeRefBase sourceType = methodArguments[0].EvaluateType();
            TypeRefBase targetType = ((Cast)_unresolvedRef.Parent).Type.EvaluateType();

            // Find the most specific source type and target type
            TypeRefBase Sx = null;
            TypeRefBase Tx = null;
            bool encompassesSource = false;
            bool encompassedByTarget = false;
            TypeRefBase mostEncompassedSource = null;
            TypeRefBase mostEncompassingSource = null;
            TypeRefBase mostEncompassedTarget = null;
            TypeRefBase mostEncompassingTarget = null;
            for (int i = 0; i < matches.Count; ++i)
            {
                MatchCandidate candidate = matches[i];
                object obj = candidate.Object;
                if (isMethodCategory || obj is MethodDecl || obj is MethodDefinition || obj is MethodInfo)
                {
                    TypeRefBase parameterTypeRef = ParameterRef.GetParameterType(MethodRef.GetParameters(obj), 0, _parentExpression);
                    TypeRefBase returnTypeRef = MethodRef.GetReturnType(obj, _parentExpression);

                    // If any of the operators in U convert from S, then Sx is S.
                    if (parameterTypeRef.IsSameRef(sourceType))
                        Sx = sourceType;
                    // If any of the operators in U convert to T, then Tx is T.
                    if (returnTypeRef.IsSameRef(targetType))
                        Tx = targetType;

                    // Otherwise, if any of the operators in U convert from types that encompass S, then Sx is the most encompassed type in the
                    // combined set of source types of those operators. If no most encompassed type can be found, then the conversion is ambiguous.
                    // Otherwise, Sx is the most encompassing type in the combined set of source types of the operators in U. If exactly one most
                    // encompassing type cannot be found, then the conversion is ambiguous.
                    if (TypeRef.IsEncompassedBy(sourceType, parameterTypeRef))
                        encompassesSource = true;

                    // Otherwise, if any of the operators in U convert to types that are encompassed by T, then Tx is the most encompassing type in the
                    // combined set of target types of those operators. If exactly one most encompassing type cannot be found, then the conversion is ambiguous.
                    // Otherwise, Tx is the most encompassed type in the combined set of target types of the operators in U. If no most encompassed type
                    // can be found, then the conversion is ambiguous.
                    if (TypeRef.IsEncompassedBy(returnTypeRef, targetType))
                        encompassedByTarget = true;

                    if (Sx == null)
                    {
                        if (i == 0)
                            mostEncompassedSource = mostEncompassingSource = parameterTypeRef;
                        else
                        {
                            if (mostEncompassedSource != null)
                            {
                                if (TypeRef.IsEncompassedBy(parameterTypeRef, mostEncompassedSource))
                                    mostEncompassedSource = parameterTypeRef;
                                else if (!TypeRef.IsEncompassedBy(mostEncompassedSource, parameterTypeRef))
                                    mostEncompassedSource = null;  // No single most encompassed source exists
                            }
                            if (mostEncompassingSource != null)
                            {
                                if (TypeRef.IsEncompassedBy(mostEncompassingSource, parameterTypeRef))
                                    mostEncompassingSource = parameterTypeRef;
                                else if (!TypeRef.IsEncompassedBy(parameterTypeRef, mostEncompassingSource))
                                    mostEncompassingSource = null;  // No single most encompassing source exists
                            }
                        }
                    }
                    if (Tx == null)
                    {
                        if (i == 0)
                            mostEncompassedTarget = mostEncompassingTarget = returnTypeRef;
                        else
                        {
                            if (mostEncompassingTarget != null)
                            {
                                if (TypeRef.IsEncompassedBy(mostEncompassingTarget, returnTypeRef))
                                    mostEncompassingTarget = returnTypeRef;
                                else if (!TypeRef.IsEncompassedBy(returnTypeRef, mostEncompassingTarget))
                                    mostEncompassingTarget = null;  // No single most encompassing target exists
                            }
                            if (mostEncompassedTarget != null)
                            {
                                if (TypeRef.IsEncompassedBy(returnTypeRef, mostEncompassedTarget))
                                    mostEncompassedTarget = returnTypeRef;
                                else if (!TypeRef.IsEncompassedBy(mostEncompassedTarget, returnTypeRef))
                                    mostEncompassedTarget = null;  // No single most encompassed target exists
                            }
                        }
                    }
                }
            }
            if (Sx == null)
                Sx = (encompassesSource ? mostEncompassedSource : mostEncompassingSource);
            if (Tx == null)
                Tx = (encompassedByTarget ? mostEncompassingTarget : mostEncompassedTarget);

            // If U contains exactly one user-defined conversion operator that converts from Sx to Tx, then this is the
            // most specific conversion operator.
            foreach (MatchCandidate candidate in matches)
            {
                object obj = candidate.Object;
                if (isMethodCategory || obj is MethodDecl || obj is MethodDefinition || obj is MethodInfo)
                {
                    TypeRefBase parameterTypeRef = ParameterRef.GetParameterType(MethodRef.GetParameters(obj), 0, _parentExpression);
                    TypeRefBase returnTypeRef = MethodRef.GetReturnType(obj, _parentExpression);
                    if (parameterTypeRef.IsSameRef(Sx) && returnTypeRef.IsSameRef(Tx))
                    {
                        if (bestConversion == null)
                            bestConversion = candidate;
                        else
                        {
                            bestConversion = null;
                            break;
                        }
                    }
                }
            }

            return bestConversion;
        }

        /// <summary>
        /// Get the IsPrivate access right of the object, and if not private then also get the IsProtected and IsInternal rights.
        /// </summary>
        protected void GetAccessRights(object obj, out bool isPrivate, out bool isProtected, out bool isInternal)
        {
            // Check for code objects
            if (obj is CodeObject)
            {
                if (obj is IModifiers)
                    ((IModifiers)obj).GetAccessRights(_unresolvedRef.IsTargetOfAssignment, out isPrivate, out isProtected, out isInternal);
                else
                    isPrivate = isProtected = isInternal = false;
                return;
            }

            // Check external objects.  For efficiency, get IsPrivate first, then continue only if necessary.
            // If anything goes wrong, we default to IsPrivate being true.
            isPrivate = true;
            isProtected = isInternal = false;
            bool isPublic = false;

            if (obj is MemberReference)
            {
                if (obj is TypeDefinition)
                {
                    TypeDefinition typeDefinition = (TypeDefinition)obj;
                    if (typeDefinition.IsNested)
                    {
                        isPrivate = typeDefinition.IsNestedPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (typeDefinition.IsNestedFamily || typeDefinition.IsNestedFamilyOrAssembly);
                            isInternal = (typeDefinition.IsNestedAssembly || typeDefinition.IsNestedFamilyOrAssembly);
                            if (!isProtected && !isInternal)
                                isPublic = typeDefinition.IsNestedPublic;
                        }
                    }
                    else
                    {
                        isPrivate = false;
                        isInternal = typeDefinition.IsNotPublic;
                        if (!isInternal)
                            isPublic = typeDefinition.IsPublic;
                    }
                }
                else if (obj is MethodDefinition)
                {
                    MethodDefinition methodDefinition = (MethodDefinition)obj;
                    isPrivate = methodDefinition.IsPrivate;
                    if (!isPrivate)
                    {
                        isProtected = (methodDefinition.IsFamily || methodDefinition.IsFamilyOrAssembly);
                        isInternal = (methodDefinition.IsAssembly || methodDefinition.IsFamilyOrAssembly);
                        if (!isProtected && !isInternal)
                            isPublic = methodDefinition.IsPublic;
                    }
                }
                else if (obj is PropertyDefinition)
                {
                    // The access rights of a property/indexer actually depend on the rights of the corresponding
                    // getter/setter, depending upon whether we're assigning to it or not.
                    PropertyDefinition propertyDefinition = (PropertyDefinition)obj;
                    MethodDefinition methodDefinition = (_unresolvedRef.IsTargetOfAssignment
                                                             ? propertyDefinition.SetMethod ?? propertyDefinition.GetMethod
                                                             : propertyDefinition.GetMethod ?? propertyDefinition.SetMethod);
                    if (methodDefinition != null)
                    {
                        isPrivate = methodDefinition.IsPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (methodDefinition.IsFamily || methodDefinition.IsFamilyOrAssembly);
                            isInternal = (methodDefinition.IsAssembly || methodDefinition.IsFamilyOrAssembly);
                            if (!isProtected && !isInternal)
                                isPublic = methodDefinition.IsPublic;
                        }
                    }
                }
                else if (obj is FieldDefinition)
                {
                    FieldDefinition fieldDefinition = (FieldDefinition)obj;
                    isPrivate = fieldDefinition.IsPrivate;
                    if (!isPrivate)
                    {
                        isProtected = (fieldDefinition.IsFamily || fieldDefinition.IsFamilyOrAssembly);
                        isInternal = (fieldDefinition.IsAssembly || fieldDefinition.IsFamilyOrAssembly);
                        if (!isProtected && !isInternal)
                            isPublic = fieldDefinition.IsPublic;
                    }
                }
                else if (obj is EventDefinition)
                {
                    // The access rights of an event actually depend on the rights of the corresponding
                    // adder/remover, depending upon whether we're assigning to it or not.
                    EventDefinition eventDefinition = (EventDefinition)obj;
                    MethodDefinition methodDefinition = (_unresolvedRef.IsTargetOfAssignment ? eventDefinition.AddMethod : eventDefinition.RemoveMethod);
                    if (methodDefinition != null)
                    {
                        isPrivate = methodDefinition.IsPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (methodDefinition.IsFamily || methodDefinition.IsFamilyOrAssembly);
                            isInternal = (methodDefinition.IsAssembly || methodDefinition.IsFamilyOrAssembly);
                            if (!isProtected && !isInternal)
                                isPublic = methodDefinition.IsPublic;
                        }
                    }
                }
                else //if (obj is GenericParameter)
                {
                    isPrivate = false;
                    isPublic = true;
                }
            }
            else if (obj is ParameterDefinition)
            {
                isPrivate = false;
                isPublic = true;
            }
            else if (obj is MemberInfo)
            {
                if (obj is Type)
                {
                    Type type = (Type)obj;
                    if (type.IsNested)
                    {
                        isPrivate = type.IsNestedPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (type.IsNestedFamily || type.IsNestedFamORAssem);
                            isInternal = (type.IsNestedAssembly || type.IsNestedFamORAssem);
                            if (!isProtected && !isInternal)
                                isPublic = type.IsNestedPublic;
                        }
                    }
                    else
                    {
                        isPrivate = false;
                        isInternal = type.IsNotPublic;
                        if (!isInternal)
                            isPublic = type.IsPublic;
                    }
                }
                else if (obj is MethodBase)
                {
                    MethodBase methodBase = (MethodBase)obj;
                    isPrivate = methodBase.IsPrivate;
                    if (!isPrivate)
                    {
                        isProtected = (methodBase.IsFamily || methodBase.IsFamilyOrAssembly);
                        isInternal = (methodBase.IsAssembly || methodBase.IsFamilyOrAssembly);
                        if (!isProtected && !isInternal)
                            isPublic = methodBase.IsPublic;
                    }
                }
                else if (obj is PropertyInfo)
                {
                    // The access rights of a property/indexer actually depend on the rights of the corresponding
                    // getter/setter, depending upon whether we're assigning to it or not.
                    PropertyInfo propertyInfo = (PropertyInfo)obj;
                    MethodInfo methodInfo = (_unresolvedRef.IsTargetOfAssignment
                                                 ? propertyInfo.GetSetMethod(true) ?? propertyInfo.GetGetMethod(true)
                                                 : propertyInfo.GetGetMethod(true) ?? propertyInfo.GetSetMethod(true));
                    if (methodInfo != null)
                    {
                        isPrivate = methodInfo.IsPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (methodInfo.IsFamily || methodInfo.IsFamilyOrAssembly);
                            isInternal = (methodInfo.IsAssembly || methodInfo.IsFamilyOrAssembly);
                            if (!isProtected && !isInternal)
                                isPublic = methodInfo.IsPublic;
                        }
                    }
                }
                else if (obj is FieldInfo)
                {
                    FieldInfo fieldInfo = (FieldInfo)obj;
                    isPrivate = fieldInfo.IsPrivate;
                    if (!isPrivate)
                    {
                        isProtected = (fieldInfo.IsFamily || fieldInfo.IsFamilyOrAssembly);
                        isInternal = (fieldInfo.IsAssembly || fieldInfo.IsFamilyOrAssembly);
                        if (!isProtected && !isInternal)
                            isPublic = fieldInfo.IsPublic;
                    }
                }
                else if (obj is EventInfo)
                {
                    // The access rights of an event actually depend on the rights of the corresponding
                    // adder/remover, depending upon whether we're assigning to it or not.
                    EventInfo eventInfo = (EventInfo)obj;
                    MethodInfo methodInfo = (_unresolvedRef.IsTargetOfAssignment ? eventInfo.GetAddMethod(true) : eventInfo.GetRemoveMethod(true));
                    if (methodInfo != null)
                    {
                        isPrivate = methodInfo.IsPrivate;
                        if (!isPrivate)
                        {
                            isProtected = (methodInfo.IsFamily || methodInfo.IsFamilyOrAssembly);
                            isInternal = (methodInfo.IsAssembly || methodInfo.IsFamilyOrAssembly);
                            if (!isProtected && !isInternal)
                                isPublic = methodInfo.IsPublic;
                        }
                    }
                }
            }
            else if (obj is ParameterInfo)
            {
                isPrivate = false;
                isPublic = true;
            }

            // It's possible for external types (from IL or other languages) to actually not have ANY
            // access rights set, so default to Private in such a situation.
            if (!isPrivate && !isProtected && !isInternal && !isPublic)
                isPrivate = true;
        }

        #region /* FIND BETTER METHOD RELATED */

        /// <summary>
        /// Find the method that better matches the specified arguments.
        /// Returns the better-matching method object, or null if neither is better.
        /// Assumes that the two candidate methods have already been determined to be valid matches for
        /// the given arguments, using implicit conversions where necessary.
        /// </summary>
        /// <param name="methodCandidate1">MatchCandidate for 1st method object.</param>
        /// <param name="methodCandidate2">MatchCandidate for 2nd method object.</param>
        /// <param name="arguments">The method arguments.</param>
        /// <param name="unresolvedRef">The parent UnresolvedRef used to track down generic type parameter types.</param>
        /// <returns>The better method object.</returns>
        protected object FindBetterMethod(MatchCandidate methodCandidate1, MatchCandidate methodCandidate2, List<Expression> arguments, UnresolvedRef unresolvedRef)
        {
            // Get the method parameters for the objects to be compared
            object method1 = methodCandidate1.Object;
            object method2 = methodCandidate2.Object;
            ICollection parameters1 = MethodRef.GetParameters(method1);
            ICollection parameters2 = MethodRef.GetParameters(method2);
            int originalParameters1Count = (parameters1 != null ? parameters1.Count : 0);
            int originalParameters2Count = (parameters2 != null ? parameters2.Count : 0);
            int parameters1Count = originalParameters1Count;
            int parameters2Count = originalParameters2Count;
            int argumentsCount = (arguments != null ? arguments.Count : 0);

            if (argumentsCount == 0)
            {
                // Special handling for an empty argument list:
                // If one method has FEWER parameters, it's better.
                if (parameters1Count < parameters2Count)
                    return method1;
                if (parameters2Count < parameters1Count)
                    return method2;
            }

            // At this point, we know we have at least one argument, so:
            // If either parameter list is empty, then the other is better, or neither if they're both empty
            if (parameters1Count == 0)
                return (parameters2Count == 0 ? null : method2);
            if (parameters2Count == 0)
                return method1;

            // Unlike the spec, we allow ref/out mismatches (although the type must match exactly) in AddMatch(),
            // relying on the analysis phase to flag them as errors.  Since it's possible to overload methods
            // with only ref/out differences, we could get dups, and need to filter them here:
            bool method1ParameterMismatch = false;
            bool method2ParameterMismatch = false;
            for (int index = 0; index < arguments.Count; ++index)
            {
                bool argIsRef = (arguments[index] is Ref);
                bool argIsOut = (arguments[index] is Out);
                bool isRef1, isOut1, isRef2, isOut2;
                ParameterRef.GetParameterType(parameters1, index, out isRef1, out isOut1);
                ParameterRef.GetParameterType(parameters2, index, out isRef2, out isOut2);
                if (!method1ParameterMismatch)
                    method1ParameterMismatch = (isRef1 != argIsRef || isOut1 != argIsOut);
                if (!method2ParameterMismatch)
                    method2ParameterMismatch = (isRef2 != argIsRef || isOut2 != argIsOut);
            }
            // Prefer a method that doesn't have ref/out mismatches over one that does
            if (!method1ParameterMismatch && method2ParameterMismatch)
                return method1;
            if (!method2ParameterMismatch && method1ParameterMismatch)
                return method2;

            // Given an argument list A with a set of argument expressions { E1, E2, ..., EN } and two applicable function
            // members MP and MQ with parameter types { P1, P2, ..., PN } and { Q1, Q2, ..., QN }, MP is defined to be a
            // better function member than MQ if:
            //   - for each argument, the implicit conversion from EX to QX is not better than the implicit conversion from EX to PX, and
            //   - for at least one argument, the conversion from EX to PX is better than the conversion from EX to QX.
            bool method1IsExpanded = false;
            bool method2IsExpanded = false;
            bool method1ParameterIsBetter = false;
            bool method2ParameterIsBetter = false;
            TypeRefBase typeRefBase1 = null;
            TypeRefBase typeRefBase2 = null;

            // Determine the parent object for purposes of evaluating type arguments.  If there are inferred type arguments
            // for either method, then move them to a temporary MethodRef so they can be used in the evaluation.
            CodeObject parent = unresolvedRef.Parent;
            parent = ((parent is Dot && ((Dot)parent).Right == unresolvedRef) ? ((Dot)parent).Left : parent.Parent);
            MethodRef method1Inferred = null;
            MethodRef method2Inferred = null;
            if (methodCandidate1.InferredTypeArguments != null)
            {
                method1Inferred = (MethodRef)unresolvedRef.CreateRef(method1, true);
                methodCandidate1.CopyInferredTypeArguments(method1Inferred);
            }
            if (methodCandidate2.InferredTypeArguments != null)
            {
                method2Inferred = (MethodRef)unresolvedRef.CreateRef(method2, true);
                methodCandidate2.CopyInferredTypeArguments(method2Inferred);
            }

            // Check the arguments of the two methods to see if one of them has better type conversions
            for (int index = 0; index < arguments.Count; ++index)
            {
                TypeRefBase argumentTypeRef = arguments[index].EvaluateType();

                // Get the parameter of each method at the current position, evaluating any type parameters,
                // and determine if the argument type has a better conversion for one of the two types.
                // Evaluate type parameters using any temporary MethodRefs with inferred types, but always
                // also check the 'parent', since there might be a generic declaring type with type arguments.
                method1IsExpanded = GetParameterType(parameters1, index, ref typeRefBase1, argumentTypeRef);
                if (method1Inferred != null)
                    typeRefBase1 = typeRefBase1.EvaluateTypeArgumentTypes(method1Inferred);
                typeRefBase1 = typeRefBase1.EvaluateTypeArgumentTypes(parent);
                method2IsExpanded = GetParameterType(parameters2, index, ref typeRefBase2, argumentTypeRef);
                if (method2Inferred != null)
                    typeRefBase2 = typeRefBase2.EvaluateTypeArgumentTypes(method2Inferred);
                typeRefBase2 = typeRefBase2.EvaluateTypeArgumentTypes(parent);
                int result = FindBetterConversion(arguments[index], typeRefBase1, typeRefBase2);
                if (result == 1)
                    method1ParameterIsBetter = true;
                else if (result == 2)
                    method2ParameterIsBetter = true;
            }

            // Dispose any temporary MethodRefs
            if (method1Inferred != null)
                method1Inferred.Dispose();
            if (method2Inferred != null)
                method2Inferred.Dispose();

            // Determine if one method is better than the other
            if (method1ParameterIsBetter && !method2ParameterIsBetter)
                return method1;
            if (method2ParameterIsBetter && !method1ParameterIsBetter)
                return method2;

            // If either method has more parameters than the available arguments, meaning that it has a params array
            // that isn't being used, then that is also considered to be "expanded form".
            if (parameters1Count > argumentsCount)
                method1IsExpanded = true;
            if (parameters2Count > argumentsCount)
                method2IsExpanded = true;

            // TIE BREAKING RULES:
            // In case the parameter type sequences {P1, P2, …, PN} and {Q1, Q2, …, QN} are equivalent (each Pi has an
            // identity conversion to the corresponding Qi), the following tie-breaking rules are applied, in order, to
            // determine the better function member:

            // If MP is a non-generic method and MQ is a generic method, then MP is better than MQ.
            bool method1Generic = IsMethodGeneric(method1);
            bool method2Generic = IsMethodGeneric(method2);
            if (!method1Generic && method2Generic)
                return method1;
            if (!method2Generic && method1Generic)
                return method2;

            // Otherwise, if MP is applicable in its normal form and MQ has a params array and is applicable only in
            // its expanded form, then MP is better than MQ.
            if (!method1IsExpanded && method2IsExpanded)
                return method1;
            if (!method2IsExpanded && method1IsExpanded)
                return method2;
            // Otherwise, if MP has more declared parameters than MQ, then MP is better than MQ. This can occur if
            // both methods have params arrays and are applicable only in their expanded forms.
            if (parameters1Count > parameters2Count)
                return method1;
            if (parameters2Count > parameters1Count)
                return method2;

            // Otherwise, if MP has more specific parameter types than MQ, then MP is better than MQ. Let {R1, R2, …, RN} and {S1, S2, …, SN}
            // represent the uninstantiated and unexpanded parameter types of MP and MQ. MP’s parameter types are more specific than MQ’s if,
            // for each parameter, RX is not less specific than SX, and, for at least one parameter, RX is more specific than SX:
            //   - A type parameter is less specific than a non-type parameter.
            //   - Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at
            //     least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
            //   - An array type is more specific than another array type (with the same number of dimensions) if the element type of the first
            //     is more specific than the element type of the second.
            //   - An array type is more specific than a non-array type.
            //     For example: M<T>(T[] p) where T=string is better than M<T>(T p) where T=string[], because T[] is more specific than T
            bool method1ParameterIsMoreSpecific = false;
            bool method2ParameterIsMoreSpecific = false;
            typeRefBase1 = null;
            typeRefBase2 = null;
            for (int index = 0; index < parameters1Count; ++index)  // The parameter counts for the methods should be the same
            {
                // The arguments count might be smaller due to an unused 'params' parameter
                TypeRefBase argumentTypeRef = (index < argumentsCount ? arguments[index].EvaluateType() : null);
                // Don't evaluate type arguments for the parameters here - we need to know if they are type arguments
                GetParameterType(parameters1, index, ref typeRefBase1, argumentTypeRef);
                GetParameterType(parameters2, index, ref typeRefBase2, argumentTypeRef);
                int result = FindMoreSpecificType(typeRefBase1, typeRefBase2);
                if (result == 1)
                    method1ParameterIsMoreSpecific = true;
                else if (result == 2)
                    method2ParameterIsMoreSpecific = true;
            }
            if (method1ParameterIsMoreSpecific && !method2ParameterIsMoreSpecific)
                return method1;
            if (method2ParameterIsMoreSpecific && !method1ParameterIsMoreSpecific)
                return method2;

            // Otherwise, neither function member is better.
            return null;
        }

        protected static bool IsMethodGeneric(object obj)
        {
            if (obj is MethodDeclBase)
                return ((MethodDeclBase)obj).IsGenericMethod;
            if (obj is MethodDefinition)
                return ((MethodDefinition)obj).HasGenericParameters;
            if (obj is MethodBase)  // MethodInfo or ConstructorInfo
                return ((MethodBase)obj).IsGenericMethod;
            return false;
        }

        /// <summary>
        /// Get the parameter at the specified index without evaluating any type parameters.
        /// </summary>
        protected static bool GetParameterType(ICollection parameters, int index, ref TypeRefBase parameterTypeRef, TypeRefBase argumentTypeRef)
        {
            // If the index exceeds the parameter collection size, abort (re-use the last type)
            if (index >= parameters.Count)
                return true;  // Indicate that expanded form is in use

            // Get the parameter type
            if (parameters is List<ParameterDecl>)
                parameterTypeRef = (((List<ParameterDecl>)parameters)[index].EvaluateType());
            else if (parameters is Collection<ParameterDefinition>)
                parameterTypeRef = TypeRef.Create(((Collection<ParameterDefinition>)parameters)[index].ParameterType);
            else //if (parameters is ParameterInfo[])
                parameterTypeRef = TypeRef.Create(((ParameterInfo[])parameters)[index].ParameterType);

            if (parameterTypeRef != null)
            {
                // Check for a 'params' parameter if we're on the last one
                if (index == parameters.Count - 1 && ParameterRef.ParameterIsParams(parameters, index) && parameterTypeRef.IsArray && argumentTypeRef != null)
                {
                    // If the argument has fewer total array ranks, or the first one isn't one-dimensional,
                    // then use the expanded form.  We specifically don't want to use the expanded form whenever
                    // the types don't match exactly for normal form, because FindBetterMethod() will then drop
                    // possible matches in favor of non-expanded ones even though there was no exact match.
                    if ((argumentTypeRef.ArrayRanks != null ? argumentTypeRef.ArrayRanks.Count : 0) < parameterTypeRef.ArrayRanks.Count || argumentTypeRef.ArrayRanks[0] != 1)
                    {
                        parameterTypeRef = parameterTypeRef.GetElementType();
                        return true;  // Indicate that expanded form is in use
                    }
                }
            }
            return false;
        }

        /// <summary>
        /// Find the better conversion of an expression to two candidate types.
        /// Returns 1 if the conversion to typeRef1 is better, 2 if the conversion to typeRef2 is better, 0 if neither is better.
        /// </summary>
        protected int FindBetterConversion(Expression expression, TypeRefBase typeRefBase1, TypeRefBase typeRefBase2)
        {
            // Given an implicit conversion C1 that converts from an expression E to a type T1, and an implicit conversion C2 that
            // converts from an expression E to a type T2, C1 is a better conversion than C2 if at least one of the following holds:
            if (expression == null)
                return 0;

            // - E has a type S and an identity conversion exists from S to T1 but not from S to T2
            TypeRefBase expressionType = expression.EvaluateType();
            bool identity1 = expressionType.ImplicitIdentityConversionExists(typeRefBase1);
            bool identity2 = expressionType.ImplicitIdentityConversionExists(typeRefBase2);
            if (identity1 && !identity2)
                return 1;
            if (identity2 && !identity1)
                return 2;

            // - E is not an anonymous function and T1 is a better conversion target than T2
            if (!(expression is AnonymousMethod))
                return FindBetterConversionTarget(typeRefBase1, typeRefBase2);

            // - E is an anonymous function, T1 is a delegate type D1, T2 is a delegate type D2 and one of the following holds:
            if (typeRefBase1.IsDelegateType && typeRefBase2.IsDelegateType)
            {
                // - D1 is a better conversion target than D2
                int result = FindBetterConversionTarget(typeRefBase1, typeRefBase2);
                if (result > 0)
                    return result;

                // - D1 and D2 have identical parameter lists, and one of the following holds:
                ICollection typeRef1Parameters = typeRefBase1.GetDelegateParameters();
                ICollection typeRef2Parameters = typeRefBase2.GetDelegateParameters();
                int typeRef1ParameterCount = (typeRef1Parameters != null ? typeRef1Parameters.Count : 0);
                if (typeRef1ParameterCount == (typeRef2Parameters != null ? typeRef2Parameters.Count : 0))
                {
                    bool parametersMatch = true;
                    for (int i = 0; i < typeRef1ParameterCount; ++i)
                    {
                        TypeRefBase parameterType1 = ParameterRef.GetParameterType(typeRef1Parameters, i, typeRefBase1);
                        TypeRefBase parameterType2 = ParameterRef.GetParameterType(typeRef2Parameters, i, typeRefBase2);
                        if (!parameterType1.IsSameRef(parameterType2))
                        {
                            parametersMatch = false;
                            break;
                        }
                    }
                    if (parametersMatch)
                    {
                        // - D1 has a return type Y1, and D2 has a return type Y2, an inferred return type X exists for E in the
                        //   context of that parameter list, and the conversion from X to Y1 is better than the conversion from X to Y2
                        // - D1 has a return type Y, and D2 is void returning
                        AnonymousMethod anonymousMethod = (AnonymousMethod)expression;
                        TypeRefBase returnType = anonymousMethod.GetReturnType();
                        TypeRefBase returnType1 = typeRefBase1.GetDelegateReturnType();
                        TypeRefBase returnType2 = typeRefBase2.GetDelegateReturnType();
                        if (returnType2 == null)
                            return (returnType1 == null ? 0 : 1);
                        if (returnType1 == null)
                            return 2;
                        returnType1 = returnType1.EvaluateTypeArgumentTypes(typeRefBase1);
                        returnType2 = returnType2.EvaluateTypeArgumentTypes(typeRefBase2);
                        bool returnType1Void = returnType1.IsSameRef(TypeRef.VoidRef);
                        bool returnType2Void = returnType2.IsSameRef(TypeRef.VoidRef);
                        if (returnType2Void && !returnType1Void)
                            return 1;
                        if (returnType1Void && !returnType2Void)
                            return 2;
                        return FindBetterConversion(returnType, returnType1, returnType2);
                    }
                }
            }

            // Neither conversion is better
            return 0;
        }

        /// <summary>
        /// Find the better conversion of a type to two candidate types.
        /// Returns 1 if the conversion to typeRefBase1 is better, 2 if the conversion to typeRefBase2 is better, 0 if neither is better.
        /// </summary>
        protected static int FindBetterConversion(TypeRefBase typeRefBase, TypeRefBase typeRefBase1, TypeRefBase typeRefBase2)
        {
            // Given a conversion C1 that converts from a type S to a type T1, and a conversion C2 that converts from
            // a type S to a type T2, C1 is a better conversion than C2 if at least one of the following holds:

            // - An identity conversion exists from S to T1 but not from S to T2
            bool identity1 = typeRefBase.ImplicitIdentityConversionExists(typeRefBase1);
            bool identity2 = typeRefBase.ImplicitIdentityConversionExists(typeRefBase2);
            if (identity1 && !identity2)
                return 1;
            if (identity2 && !identity1)
                return 2;

            // - T1 is a better conversion target than T2
            return FindBetterConversionTarget(typeRefBase1, typeRefBase2);
        }

        /// <summary>
        /// Find the better conversion target of two candidate types.
        /// Returns 1 if typeRefBase1 is better, 2 if typeRefBase2 is better, 0 if neither is better.
        /// </summary>
        protected static int FindBetterConversionTarget(TypeRefBase typeRefBase1, TypeRefBase typeRefBase2)
        {
            // Given two different types T1 and T2, T1 is a better conversion target than T2 if at least one of the following holds:

            // - An implicit conversion from T1 to T2 exists, and no implicit conversion from T2 to T1 exists
            bool convert1To2 = typeRefBase1.IsImplicitlyConvertibleTo(typeRefBase2);
            bool convert2To1 = typeRefBase2.IsImplicitlyConvertibleTo(typeRefBase1);
            if (convert1To2 && !convert2To1)
                return 1;
            if (convert2To1 && !convert1To2)
                return 2;

            if (typeRefBase1 is TypeRef && typeRefBase2 is TypeRef)
            {
                TypeRef typeRef1 = (TypeRef)typeRefBase1;
                TypeRef typeRef2 = (TypeRef)typeRefBase2;

                // - T1 is a signed integral type and T2 is an unsigned integral type or vice-versa. Specifically:
                if (typeRef1.IsPrimitive && typeRef2.IsPrimitive)
                {
                    TypeCode typeCode1 = typeRef1.GetTypeCode();
                    TypeCode typeCode2 = typeRef2.GetTypeCode();

                    // - T1 is sbyte and T2 is byte, ushort, uint, or ulong
                    if (typeCode1 == TypeCode.SByte && (typeCode2 == TypeCode.Byte || typeCode2 == TypeCode.UInt16 || typeCode2 == TypeCode.UInt32 || typeCode2 == TypeCode.UInt64))
                        return 1;
                    if (typeCode2 == TypeCode.SByte && (typeCode1 == TypeCode.Byte || typeCode1 == TypeCode.UInt16 || typeCode1 == TypeCode.UInt32 || typeCode1 == TypeCode.UInt64))
                        return 2;
                    // - T1 is short and T2 is ushort, uint, or ulong
                    if (typeCode1 == TypeCode.Int16 && (typeCode2 == TypeCode.UInt16 || typeCode2 == TypeCode.UInt32 || typeCode2 == TypeCode.UInt64))
                        return 1;
                    if (typeCode2 == TypeCode.Int16 && (typeCode1 == TypeCode.UInt16 || typeCode1 == TypeCode.UInt32 || typeCode1 == TypeCode.UInt64))
                        return 2;
                    // - T1 is int and T2 is uint, or ulong
                    if (typeCode1 == TypeCode.Int32 && (typeCode2 == TypeCode.UInt32 || typeCode2 == TypeCode.UInt64))
                        return 1;
                    if (typeCode2 == TypeCode.Int32 && (typeCode1 == TypeCode.UInt32 || typeCode1 == TypeCode.UInt64))
                        return 2;
                    // - T1 is long and T2 is ulong
                    if (typeCode1 == TypeCode.Int64 && typeCode2 == TypeCode.UInt64)
                        return 1;
                    if (typeCode2 == TypeCode.Int64 && typeCode1 == TypeCode.UInt64)
                        return 2;
                }
            }

            // Neither conversion is better
            return 0;
        }

        /// <summary>
        /// Find the more specific of two candidate types.
        /// Returns 1 if typeRef1 is more specific, 2 if typeRef2 is more specific, 0 if neither is more specific.
        /// </summary>
        protected static int FindMoreSpecificType(TypeRefBase typeRefBase1, TypeRefBase typeRefBase2)
        {
            // Handle unresolved types
            if (typeRefBase1 == null || typeRefBase2 == null || typeRefBase1 is UnresolvedRef || typeRefBase2 is UnresolvedRef)
                return 0;

            TypeRef typeRef1 = (TypeRef)typeRefBase1;
            TypeRef typeRef2 = (TypeRef)typeRefBase2;

            // A type parameter is less specific than a non-type parameter.
            if (!typeRef1.IsGenericParameter && typeRef2.IsGenericParameter)
                return 1;
            if (!typeRef2.IsGenericParameter && typeRef1.IsGenericParameter)
                return 2;

            // Recursively, a constructed type is more specific than another constructed type (with the same number of type arguments) if at
            // least one type argument is more specific and no type argument is less specific than the corresponding type argument in the other.
            if (typeRef1.IsGenericType && typeRef2.IsGenericType)
            {
                ChildList<Expression> type1Arguments = typeRef1.TypeArguments;
                ChildList<Expression> type2Arguments = typeRef2.TypeArguments;
                if (type1Arguments.Count == type2Arguments.Count)
                {
                    bool type1ArgumentIsMoreSpecific = false;
                    bool type2ArgumentIsMoreSpecific = false;
                    for (int index = 0; index < type1Arguments.Count; ++index)
                    {
                        int result = FindMoreSpecificType(type1Arguments[index].EvaluateType(), type2Arguments[index].EvaluateType());
                        if (result == 1)
                            type1ArgumentIsMoreSpecific = true;
                        else if (result == 2)
                            type2ArgumentIsMoreSpecific = true;
                    }
                    if (type1ArgumentIsMoreSpecific && !type2ArgumentIsMoreSpecific)
                        return 1;
                    if (type2ArgumentIsMoreSpecific && !type1ArgumentIsMoreSpecific)
                        return 2;
                }
            }

            if (typeRef1.IsArray)
            {
                if (typeRef2.IsArray)
                {
                    // An array type is more specific than another array type (with the same number of dimensions) if the element type of the first
                    // is more specific than the element type of the second.
                    if (CollectionUtil.CompareList(typeRef1.ArrayRanks, typeRef2.ArrayRanks))
                        return FindMoreSpecificType(typeRef1.GetElementType(), typeRef2.GetElementType());
                }
                else
                    return 1;  // An array type is more specific than a non-array type
            }
            else if (typeRef2.IsArray)
                return 2;  // An array type is more specific than a non-array type

            return 0;
        }

        #endregion

        #endregion

        #region /* ADD MATCH RELATED */

        /// <summary>
        /// Add matching code object(s).
        /// </summary>
        /// <param name="obj">The CodeObject, MemberInfo, ICollection, or null.</param>
        public void AddMatch(object obj)
        {
            if (obj != null)
            {
                if (obj is IEnumerable)
                {
                    // Breakup collections into individual objects
                    foreach (object @object in (IEnumerable)obj)
                        AddMatchInternal(@object);
                }
                else
                    AddMatchInternal(obj);
            }
        }

        protected void AddMatchInternal(object obj, bool lookInAllParts, TypeRefBase parentTypeRef)
        {
            // Also treat a Method category as a constructor category if we're in a DocCodeRefBase
            bool isConstructorCategory = (ResolveCategoryHelpers.IsConstructor[(int)_resolveCategory] || IsDocCodeRefToMethod);

            // If we're looking for a constructor and we found a type with the same name, get the constructors on the type instead
            if (isConstructorCategory && (obj is ITypeDecl || obj is TypeDefinition || obj is Type))
            {
                // If the type is a partial type, then only look in all parts if instructed (this is used by NewObject when looking for
                // all constructors on the already-resolved TypeRef, otherwise we only want to find constructors on the current part,
                // since we will end up searching, finding, and calling this method for all parts of the type).
                NamedCodeObjectGroup constructors = TypeRef.GetConstructors(obj, !lookInAllParts);
                if (constructors != null && constructors.Count > 0)
                {
                    // We may have found constructors from inside the type on the way up the code tree, so only add the constructors
                    // if the first one doesn't already exist.  This can only occur for ITypeDecls, not for TypeDefinition/Types.
                    if (obj is TypeDefinition || obj is Type || !_unresolvedRef.HasMatches || !_unresolvedRef.Matches.Contains((CodeObject)constructors[0]))
                        AddMatch(constructors);
                }
                else
                {
                    // If there aren't any constructors on the type, check for a CoClassAttribute type, and add it if found
                    object coClassType = GetCoClassAttributeType(obj);
                    if (coClassType != null)
                        AddMatchInternal(coClassType, lookInAllParts, parentTypeRef);
                }

                return;
            }

            // Create a wrapper object for the match candidate
            MatchCandidate candidate = new MatchCandidate(obj, _unresolvedRef, _resolveFlags);

            // Check that the type of the object is valid for the category (if the category is null, allow it to pass - this
            // is used by UnresolvedRef.ResolveMethodGroup() when resolving method groups against a delegate type).
            if (_isValidCategory == null || _isValidCategory(obj))
            {
                candidate.IsCategoryMatch = true;

                // If there are type arguments, the match isn't complete if the counts don't match
                if (!DoTypeArgumentCountsMatch(obj))
                    candidate.IsTypeArgumentsMatch = false;

                bool isMethodCategory = ResolveCategoryHelpers.IsMethod[(int)_resolveCategory];

                // If there are method arguments, the match isn't complete unless they match.
                // If we have a Method category, then we know that we're resolving a method or delegate invocation.  Our parent
                // will be an ArgumentsOperator (Call, Index, or NewObject) - or our grandparent if our parent is a Dot - but we
                // don't need to verify that here (the subroutine below will take care of that).  We check the category instead
                // of the type of the object because it's easier than checking all the Method category delegate types.
                // If we have a method object, but our category wasn't Method, that means the category is Expression (we don't
                // need to verify that), and we've got a "method group" being passed as an argument or initializing a variable.
                // In this case, we need to find the delegate type we're being assigned to, and get our "arguments" from it.
                if (isMethodCategory || candidate.IsMethod)
                {
                    // Ignore overridden methods.  We must ignore 'override' methods so that we match the 'virtual' declaration
                    // in the base class, so that any overloads in the base class are also considered (per the spec).
                    if (MethodRef.IsOverridden(obj))
                        return;

                    bool argumentsMismatchDueToUnresolvedOnly = true;

                    // Get the method arguments
                    bool hasUnresolvedDelegateTypes;
                    List<Expression> methodArguments = GetParentArguments(isMethodCategory, out hasUnresolvedDelegateTypes);
                    if (methodArguments == null)
                    {
                        // Null means either no arguments exist (no parens were specified AND no associated delegate type exists,
                        // such as "MethodName is object" or "MethodName" in a doc comment cref), or they couldn't be determined
                        // due to the associated delegate type being unresolved.  Treat the first as a match, the 2nd as an error.
                        if (hasUnresolvedDelegateTypes)
                            candidate.IsMethodArgumentsMatch = false;
                    }
                    else
                    {
                        // Get the parameters of the method or delegate type
                        ICollection parameters = MethodRef.GetParameters(obj, _unresolvedRef);
                        int parameterCount = (parameters != null ? parameters.Count : 0);

                        // Perform Type Inference if the candidate has inferred types
                        if (candidate.InferredTypeArguments != null && parameterCount > 0)
                        {
                            // For now, if type inference fails OR if any of the inferred types are unresolved, treat it as the type arguments not matching, so
                            // that it will be displayed as an error (instead of possibly as a warning).  We might want to eventually improve the error reporting
                            // to distinguish between these events and normal type argument mismatches.
                            if (candidate.InferTypeArguments(parameters, methodArguments, _unresolvedRef))
                            {
                                // Evaluate any inferred OpenTypeParameterRefs in any parent expression or type declarations now that
                                // type inference is complete (this would cause problems if it were done during type inference).
                                for (int i = 0; i < candidate.InferredTypeArguments.Length; ++i)
                                {
                                    TypeRefBase typeRefBase = candidate.InferredTypeArguments[i];
                                    if (typeRefBase is OpenTypeParameterRef)
                                        candidate.InferredTypeArguments[i] = typeRefBase.EvaluateTypeArgumentTypes(_unresolvedRef.Parent, _unresolvedRef);
                                }

                                // Fail if any of the inferred types are unresolved (unless we're in a doc comment)
                                if (Enumerable.Any(candidate.InferredTypeArguments, delegate(TypeRefBase typeRef) { return typeRef.HasUnresolvedRef(); }) && !_resolveFlags.HasFlag(ResolveFlags.InDocComment))
                                    candidate.IsTypeInferenceFailure = true;
                            }
                            else
                                candidate.IsTypeInferenceFailure = true;
                        }

                        // Determine if the parameter count and types match.  A special first pass (-1) is done to match or
                        // eliminate zero parameters, and a final pass (maxIndex) eliminates extra parameters.
                        TypeRefBase paramsTypeRef = null;
                        int argumentCount = methodArguments.Count;
                        int maxIndex = (argumentCount == 0 || parameterCount == 0 ? -1 : argumentCount);
                        for (int i = -1; i <= maxIndex; ++i)
                        {
                            bool parameterMatches;
                            bool failedBecauseUnresolved = false;
                            if (i == -1)
                            {
                                // An index of -1 means we're matching or excluding an empty parameter list
                                if (argumentCount == 0)
                                    parameterMatches = (parameterCount == 0 || (parameterCount == 1 && ParameterRef.ParameterIsParams(parameters, 0)));
                                else
                                {
                                    parameterMatches = (parameterCount > 0);
                                    if (!parameterMatches)
                                    {
                                        // Check if we failed to match because of a variable with an unresolved type (instead of a delegate type)
                                        if (obj is IVariableDecl && ((CodeObject)obj).HasUnresolvedRef())
                                            failedBecauseUnresolved = true;
                                    }
                                }
                            }
                            else if (i == maxIndex)
                            {
                                // An index of 'maxIndex' means we're matching on the total number of arguments.
                                // We also have to check for a 'params' parameter (used more than once, or unused).
                                parameterMatches = ((i == parameterCount) || (i >= parameterCount - 1 && ParameterRef.ParameterIsParams(parameters, parameterCount - 1)));
                            }
                            else
                            {
                                // Evaluate the argument type, and any type arguments if it's a nested generic type
                                TypeRefBase argumentTypeRef = methodArguments[i].EvaluateType();
                                if (argumentTypeRef is TypeRef && ((TypeRef)argumentTypeRef).IsNested && ((TypeRef)argumentTypeRef).IsGenericType)
                                    argumentTypeRef = argumentTypeRef.EvaluateTypeArgumentTypes(_unresolvedRef.Parent, _unresolvedRef);

                                // Check if the type of the current argument matches the method parameter type
                                parameterMatches = ParameterRef.MatchParameter(_unresolvedRef, obj, candidate, parameters, i,
                                    argumentCount, argumentTypeRef, ref paramsTypeRef, true, _parentExpression, out failedBecauseUnresolved);
                            }
                            if (!parameterMatches)
                            {
                                // Mark the first argument that fails to match
                                if (candidate.IsMethodArgumentsMatch)
                                {
                                    candidate.IsMethodArgumentsMatch = false;
                                    candidate.MethodArgumentMismatchIndex = i;
                                }
                                // If the failure was due to something other than an unresolved parameter, record
                                // it as such and skip checking any more parameters.
                                if (!failedBecauseUnresolved)
                                {
                                    argumentsMismatchDueToUnresolvedOnly = false;
                                    break;
                                }
                            }
                        }
                    }
                    if (candidate.IsMethodArgumentsMatch)
                    {
                        // For user-defined explicit conversions, verify that the operator is applicable
                        if (_resolveCategory == ResolveCategory.OperatorOverload && _unresolvedRef.Name == "op_Explicit" && _unresolvedRef.Parent is Cast)
                        {
                            // Find operators that convert from a type encompassing or encompassed by S to a type encompassing or encompassed by T
                            TypeRefBase sourceTypeRef = methodArguments[0].EvaluateType();
                            TypeRefBase targetTypeRef = ((Cast)_unresolvedRef.Parent).Type.EvaluateType();
                            TypeRefBase parameterTypeRef = ParameterRef.GetParameterType(MethodRef.GetParameters(obj), 0, _parentExpression);
                            TypeRefBase returnTypeRef = MethodRef.GetReturnType(obj, _parentExpression);
                            bool matches = ((TypeRef.IsEncompassedBy(sourceTypeRef, parameterTypeRef) || TypeRef.IsEncompassedBy(parameterTypeRef, sourceTypeRef))
                                            && (TypeRef.IsEncompassedBy(targetTypeRef, returnTypeRef) || TypeRef.IsEncompassedBy(returnTypeRef, targetTypeRef)));
                            if (!matches && targetTypeRef is TypeRef && ((TypeRef)targetTypeRef).IsEnum)
                            {
                                // If the return type wasn't convertible to the cast type, and the cast type is an enum, get the underlying type
                                // of the enum and try again.
                                targetTypeRef = ((TypeRef)targetTypeRef).GetUnderlyingTypeOfEnum();
                                matches = (TypeRef.IsEncompassedBy(targetTypeRef, returnTypeRef) || TypeRef.IsEncompassedBy(returnTypeRef, targetTypeRef));
                            }
                            if (!matches)
                                candidate.IsMethodArgumentsMatch = false;  // Treat as an argument mismatch
                        }
                    }
                    else
                        candidate.ArgumentsMismatchDueToUnresolvedOnly = argumentsMismatchDueToUnresolvedOnly;
                }

                // Verify that the static modifier matches for cases where it matters:
                // If we have a parent expression, and the reference is to a property or method, static matters - if
                // the parent is a TypeRef, the reference must be static, otherwise it must not be.  If the reference
                // is a constructor, it must not be static (even though it might have a TypeRef for a parent if it's the
                // constructor of a nested type).  In all other situations, the reference can be either static or non-static.
                // Specifically, if the reference is to a type or namespace, don't check the static mode.
                // If we're evaluating a reference inside a 'cref' doc comment, ignore static checking (thus allowing static
                // references to non-static type members).
                if (((_parentExpression != null && ResolveCategoryHelpers.IsPropertyOrMethod[(int)_resolveCategory])
                    || isConstructorCategory) && !(obj is ITypeDecl || obj is TypeDefinition || obj is Type || obj is Namespace) && !_resolveFlags.HasFlag(ResolveFlags.InDocCodeRef))
                {
                    // Default to requiring the reference to NOT be static
                    bool isStatic = false;
                    if (!isConstructorCategory)
                    {
                        // If the parent expression is a TypeRef, then the reference must be static or const
                        TypeRef typeRef = (_parentExpression.SkipPrefixes() as TypeRef);
                        if (typeRef != null)
                        {
                            // If the parent expression is an Interface (an explicit interface implementation, or
                            // a bogus reference to a static member of Object, such as ReferenceEquals), then abort
                            // (don't check the static mode after all).
                            if (typeRef.IsInterface)
                                goto skip;
                            isStatic = true;
                        }
                    }

                    // Verify that the static modifier matches
                    candidate.IsStaticModeMatch = (IsStatic(obj) == isStatic);
                }
            skip:

                // Verify that the match is accessible - if it's not, we must treat it as an incomplete match so that
                // we'll continue looking for other possible matches that are complete and accessible.  For example,
                // multiple overloaded methods (with or without matching parameters) where a private one in a derived
                // class should be ignored in favor of others in base classes, or non-private events that are backed by
                // private delegate fields with the same name in the same class, or invalid code where two members are
                // otherwise duplicates but have different access rights.

                // We have to call the individual access right properties to determine access instead of looking at the
                // Modifiers bit flags, in order to handle default access rights and the actual access rights of property/
                // indexer/event accessors.  Use a special method for performance that gets the access rights that we need.
                bool isPrivate, isProtected, isInternal;
                GetAccessRights(obj, out isPrivate, out isProtected, out isInternal);
                if (isPrivate)
                {
                    // If the ref is inside the same TypeDecl as the definition OR a nested type, then it's accessible, otherwise it's not
                    if (obj is INamedCodeObject)
                    {
                        TypeDecl refDeclaringTypeDecl = _unresolvedRef.FindParent<TypeDecl>();
                        if (refDeclaringTypeDecl != null)
                        {
                            TypeDecl objDeclaringTypeDecl = ((INamedCodeObject)obj).FindParent<TypeDecl>() ?? obj as TypeDecl;
                            // Check if the TypeDecls are identical OR have the same name (could be different parts)
                            candidate.IsAccessible = refDeclaringTypeDecl.IsSameAs(objDeclaringTypeDecl);
                            while (!candidate.IsAccessible && refDeclaringTypeDecl.Parent is TypeDecl)
                            {
                                refDeclaringTypeDecl = (TypeDecl)refDeclaringTypeDecl.Parent;
                                if (refDeclaringTypeDecl.IsSameAs(objDeclaringTypeDecl))
                                    candidate.IsAccessible = true;
                            }
                        }
                    }
                    else
                        candidate.IsAccessible = false;
                }
                else
                {
                    bool isInternalAccessible = true, isProtectedAccessible = true;
                    if (isInternal)
                    {
                        // If the reference is in the same Project as the definition OR it's in another project or assembly that has
                        // specifically made its internals visible to the referring project, then it's accessible.
                        Project refDeclaringProject = _unresolvedRef.FindParent<Project>();
                        if (refDeclaringProject != null)
                        {
                            if (obj is INamedCodeObject)
                            {
                                Project objDeclaringProject = ((INamedCodeObject)obj).FindParent<Project>();
                                isInternalAccessible = (refDeclaringProject == objDeclaringProject || objDeclaringProject.AreInternalTypesVisibleTo(refDeclaringProject));
                            }
                            else
                            {
                                string assemblyName = null;
                                if (obj is MemberReference)
                                    assemblyName = ((MemberReference)obj).Module.Assembly.FullName;
                                else if (obj is MemberInfo)
                                    assemblyName = ((MemberInfo)obj).Module.Assembly.FullName;
                                if (assemblyName != null)
                                {
                                    assemblyName = AssemblyUtil.GetNormalizedDisplayName(assemblyName);
                                    LoadedAssembly objDeclaringAssembly = refDeclaringProject.FrameworkContext.ApplicationContext.FindLoadedAssembly(assemblyName);
                                    isInternalAccessible = (objDeclaringAssembly != null && objDeclaringAssembly.AreInternalTypesVisibleTo(refDeclaringProject));
                                }
                            }
                        }
                    }
                    if (isProtected)
                    {
                        // If the ref is inside the same type as the definition or any derived type OR nested type, then it's accessible
                        TypeDecl refDeclaringTypeDecl = _unresolvedRef.FindParent<TypeDecl>();
                        if (refDeclaringTypeDecl != null)
                        {
                            object objDeclaringType = (obj is INamedCodeObject ? (((INamedCodeObject)obj).FindParent<TypeDecl>() ?? obj as TypeDecl)
                                : (obj is MemberReference ? (object)((MemberReference)obj).DeclaringType : ((MemberInfo)obj).DeclaringType));
                            isProtectedAccessible = IsSameOrBaseTypeOf(objDeclaringType, refDeclaringTypeDecl);
                            while (!isProtectedAccessible && refDeclaringTypeDecl.Parent is TypeDecl)
                            {
                                refDeclaringTypeDecl = (TypeDecl)refDeclaringTypeDecl.Parent;
                                if (IsSameOrBaseTypeOf(objDeclaringType, refDeclaringTypeDecl))
                                    isProtectedAccessible = true;
                            }
                        }
                    }
                    if (isInternal)
                    {
                        if (isProtected)
                        {
                            if (!isInternalAccessible && !isProtectedAccessible)
                                candidate.IsAccessible = false;
                        }
                        else
                        {
                            if (!isInternalAccessible)
                                candidate.IsAccessible = false;
                        }
                    }
                    else if (isProtected && !isProtectedAccessible)
                        candidate.IsAccessible = false;
                }
            }

            // Conditionally add the candidate to the collection (depending upon what is already in it)
            _hasCompleteMatch = _unresolvedRef.AddMatch(candidate);
        }

        protected void AddMatchInternal(object obj, bool lookInAllParts)
        {
            AddMatchInternal(obj, lookInAllParts, null);
        }

        protected void AddMatchInternal(object obj)
        {
            AddMatchInternal(obj, false, null);
        }

        private bool IsSameOrBaseTypeOf(object typeReference, TypeDecl typeDecl)
        {
            if (typeReference is TypeDecl && ((TypeDecl)typeReference).IsSameAs(typeDecl))
                return true;
            TypeRef typeRef = (TypeRef)TypeRef.CreateTypeRef(typeReference, null, false);
            TypeRef baseTypeRef = typeDecl.GetBaseType();
            do
            {
                if (typeRef.IsSameGenericType(baseTypeRef))
                    return true;
                baseTypeRef = baseTypeRef.GetBaseType() as TypeRef;
            }
            while (baseTypeRef != null);
            return false;
        }

        /// <summary>
        /// Get any CoClass attribute type for the specified object.
        /// </summary>
        protected static object GetCoClassAttributeType(object obj)
        {
            const string CoClassAttributeName = "CoClassAttribute";
            object coClassType = null;
            if (obj is ITypeDecl)
            {
                Expression attributeExpression = ((ITypeDecl)obj).GetAttribute(CoClassAttributeName);
                if (attributeExpression is Call)
                {
                    ChildList<Expression> arguments = ((Call)attributeExpression).Arguments;
                    if (arguments != null && arguments.Count > 0 && arguments[0] is TypeOf)
                        coClassType = ((TypeOf)arguments[0]).Expression.EvaluateType().Reference;
                }
            }
            else if (obj is TypeDefinition)
            {
                CustomAttribute customAttribute = ICustomAttributeProviderUtil.GetCustomAttribute((TypeDefinition)obj, CoClassAttributeName);
                if (customAttribute != null)
                    coClassType = customAttribute.ConstructorArguments[0].Value;
            }
            else //if (obj is Type)
            {
                CustomAttributeData customAttributeData = MemberInfoUtil.GetCustomAttribute((Type)obj, CoClassAttributeName);
                if (customAttributeData != null)
                    coClassType = customAttributeData.ConstructorArguments[0].Value;
            }
            return coClassType;
        }

        /// <summary>
        /// Check if the type arguments match, based upon the object type.
        /// </summary>
        protected bool DoTypeArgumentCountsMatch(object obj)
        {
            if (obj is CodeObject)
            {
                if (obj is ITypeParameters)
                    return ((ITypeParameters)obj).DoTypeArgumentCountsMatch(_unresolvedRef);
                return (_unresolvedRef.TypeArgumentCount == 0);
            }

            int typeArgumentCount = _unresolvedRef.TypeArgumentCount;
            if (obj is TypeDefinition)
                return (TypeDefinitionUtil.GetLocalGenericArgumentCount((TypeDefinition)obj) == typeArgumentCount);
            if (obj is MethodDefinition)
            {
                MethodDefinition methodDefinition = (MethodDefinition)obj;
                int typeParameterCount = methodDefinition.GenericParameters.Count;
                if (typeParameterCount == typeArgumentCount)
                    return true;
                if (typeArgumentCount == 0)
                {
                    // Type arguments to generic methods can be omitted and inferred from the parameter types, so if
                    // the actual count is 0, it's still considered a match *if* we have at least one method parameter.
                    if (methodDefinition.Parameters != null && methodDefinition.Parameters.Count > 0)
                        return true;

                    // If the UnresolvedRef is part of an explicit interface implementation of a GenericMethodDecl,
                    // then match the actual type argument count.
                    if (_unresolvedRef.IsExplicitInterfaceImplementation && _unresolvedRef.Parent.Parent is GenericMethodDecl)
                        return (typeParameterCount == ((GenericMethodDecl)_unresolvedRef.Parent.Parent).TypeParameterCount);
                }
                return false;
            }
            if (obj is Type)
                return (TypeUtil.GetLocalGenericArgumentCount((Type)obj) == typeArgumentCount);
            if (obj is MethodInfo)
            {
                MethodInfo methodInfo = (MethodInfo)obj;
                int typeParameterCount = methodInfo.GetGenericArguments().Length;
                if (typeParameterCount == typeArgumentCount)
                    return true;
                if (typeArgumentCount == 0)
                {
                    // Type arguments to generic methods can be omitted and inferred from the parameter types, so if
                    // the actual count is 0, it's still considered a match *if* we have at least one method parameter.
                    if (methodInfo.GetParameters().Length > 0)
                        return true;

                    // If the UnresolvedRef is part of an explicit interface implementation of a GenericMethodDecl,
                    // then match the actual type argument count.
                    if (_unresolvedRef.IsExplicitInterfaceImplementation && _unresolvedRef.Parent.Parent is GenericMethodDecl)
                        return (typeParameterCount == ((GenericMethodDecl)_unresolvedRef.Parent.Parent).TypeParameterCount);
                }
                return false;
            }

            // Other object types can't have type arguments, so the count must be 0
            return (typeArgumentCount == 0);
        }

        #endregion

        /// <summary>
        /// Get the arguments from the parent object.
        /// </summary>
        /// <param name="hasArguments">False if we're looking for the parameters of a delegate type to
        /// which a method group is being assigned.</param>
        /// <param name="hasUnresolvedDelegateType">True if the arguments couldn't be returned because the
        /// associated delegate type is unresolved.</param>
        /// <returns>The list of arguments.  An empty list is returned if there are zero arguments, or null
        /// is returned if no arguments exist or if they can't be determined due to an unresolved delegate type.</returns>
        protected List<Expression> GetParentArguments(bool hasArguments, out bool hasUnresolvedDelegateType)
        {
            hasUnresolvedDelegateType = false;

            // Get the parent object, or grandparent if we have a Dot or a CheckedOperator (which can wrap the method
            // name, before the call).  Also skip a Conditional parent, allowing for a Return expression with one.
            bool hasDot = false;
            CodeObject lastObject = _unresolvedRef;
            CodeObject parent = _unresolvedRef.Parent;
            if (parent is Dot)
            {
                lastObject = parent;
                parent = parent.Parent;
                hasDot = true;
            }
            if (parent is CheckedOperator || parent is Conditional)
            {
                lastObject = parent;
                parent = parent.Parent;
            }

            // Find the arguments based upon the parent object type
            List<Expression> arguments = null;
            if (parent is ArgumentsOperator)
            {
                ArgumentsOperator argumentsOperator = (ArgumentsOperator)parent;
                if (hasArguments)
                {
                    // Handle the most common case - a method (or delegate) invocation.  We return the actual
                    // ChildList for performance - it's up to the caller to not modify the list in this case.
                    arguments = argumentsOperator.Arguments;

                    // If the list is null, create a dummy empty one to distinguish from the absense of arguments
                    if (arguments == null)
                        arguments = new List<Expression>();

                    // If they're Attribute arguments, extract from the list if necessary to remove any property assignments
                    if (argumentsOperator is Call && argumentsOperator.Parent is Attribute)
                    {
                        int argumentCount = Enumerable.Count(Enumerable.TakeWhile(arguments, delegate(Expression argument) { return !(argument is Assignment); }));
                        return (argumentCount < arguments.Count ? arguments.GetRange(0, argumentCount) : arguments);
                    }
                }
                else
                {
                    // Handle a method group being passed as an argument to a parameter of a delegate type.
                    // The method group can only represent methods (or generic methods) and not constructors, destructors,
                    // or indexers.  However, our parent ArgumentsOperator might be a Call, Index, or NewObject.
                    for (int i = 0; i < argumentsOperator.Arguments.Count; ++i)
                    {
                        Expression argument = argumentsOperator.Arguments[i];
                        if (argument == lastObject)
                        {
                            SymbolicRef targetRef = argumentsOperator.GetInvocationTargetRef();
                            if (targetRef == null || targetRef is UnresolvedRef)
                            {
                                // The arguments couldn't be determined because the delegate type is not resolved yet
                                hasUnresolvedDelegateType = true;
                                return null;
                            }
                            TypeRefBase delegateTypeRef = argumentsOperator.GetDelegateParameterType(targetRef.Reference, i);
                            arguments = GetDelegateParametersAsArguments(delegateTypeRef);
                            break;
                        }
                    }
                }
            }
            else if (parent is Attribute)
            {
                // Handle the case of an attribute with any parens on the call
                arguments = new List<Expression>();
            }
            else if (parent is VariableDecl)
            {
                // Handle a method group in a variable initialization - get the parameters of the delegate
                // type to which we're being assigned.
                if (!hasArguments)
                    arguments = GetDelegateParametersAsArguments(((VariableDecl)parent).Type);
            }
            else if (parent is TypeRefBase)
            {
                // If our parent is a TypeRefBase, we're doing an overload resolution of an existing method group, smuggling the
                // associated delegate type in as our Parent.  See UnresolvedRef.ResolveMethodGroup().
                arguments = GetDelegateParametersAsArguments((TypeRefBase)parent);
            }
            else if (parent is IParameters)
            {
                if (hasDot)
                {
                    // Handle the special case of an explicit interface implementation of a method or
                    // indexer - we have to convert the parameters to arguments so they can be matched.
                    // We MUST provide an empty list if there are zero parameters.
                    arguments = new List<Expression>();
                    ChildList<ParameterDecl> parameterDecls = ((IParameters)parent).Parameters;
                    if (parameterDecls != null && parameterDecls.Count > 0)
                        arguments.AddRange(Enumerable.Select<ParameterDecl, Expression>(parameterDecls, delegate(ParameterDecl parameterDecl) { return parameterDecl.Type; }));
                }
            }
            else if (parent is BinaryOperator)
            {
                BinaryOperator binaryOperator = (BinaryOperator)parent;
                if (binaryOperator.HiddenRef == _unresolvedRef)
                {
                    // Handle the case of checking for an overloaded binary operator - in this case, the
                    // arguments are the operands of the operator.
                    arguments = new List<Expression>(2) { binaryOperator.Left, binaryOperator.Right };
                }
                else
                {
                    // Handle a method group on the right side of a binary operator - such as a method group being
                    // assigned to a delegate, or added/removed from an event with "+=" or "-=", or simply compared
                    // to a delegate variable with "==" or "!=".  Note that for the last example, the method group
                    // could be on the left side - although it's poor programming style, and also can't resolve on
                    // the first pass since the right side won't be resolved yet.  Regardless, we must check for the
                    // left side scenario here and pre-resolve the right side.
                    if (binaryOperator.Left != null)
                    {
                        if (binaryOperator.Left.SkipPrefixes() == _unresolvedRef)
                        {
                            if (binaryOperator.Right != null)
                            {
                                binaryOperator.Right = (Expression)binaryOperator.Right.Resolve(ResolveCategory.Expression, _resolveFlags);
                                arguments = GetDelegateParametersAsArguments(binaryOperator.Right.EvaluateType());
                            }
                        }
                        else
                            arguments = GetDelegateParametersAsArguments(binaryOperator.Left.EvaluateType());
                    }
                }
            }
            else if (parent is UnaryOperator)
            {
                // Handle the case of checking for an overloaded unary operator - in this case, the
                // argument is the operand of the operator.
                if (parent.HiddenRef == _unresolvedRef)
                    arguments = new List<Expression>(1) { ((UnaryOperator)parent).Expression };
                else if (parent is Cast)
                {
                    // Handle casting to a delegate type
                    arguments = GetDelegateParametersAsArguments(((Cast)parent).Type.EvaluateType());
                }
            }
            else if (parent is Return)
            {
                // If the parent is a Return statement, get the delegate type from the parent method return type,
                // so that we can resolve overloaded methods based on the parent's return type.
                Expression delegateExpression = null;
                CodeObject parentMethod = parent.FindParentMethod();
                if (parentMethod is MethodDeclBase)
                    delegateExpression = ((MethodDeclBase)parentMethod).ReturnType;
                else if (parentMethod is AnonymousMethod)
                {
                    // If the anonymous method is being returned from another anonymous method, recursively get the parent
                    // delegate expression of the parent anonymous method, then get the return type of that delegate.
                    delegateExpression = ((AnonymousMethod)parentMethod).GetParentDelegateExpression();
                    if (delegateExpression != null)
                        delegateExpression = delegateExpression.GetDelegateReturnType();
                }
                if (delegateExpression != null)
                    arguments = GetDelegateParametersAsArguments(delegateExpression);
            }

            return arguments;
        }

        protected static List<Expression> GetDelegateParametersAsArguments(Expression parentExpression)
        {
            List<Expression> arguments = null;
            if (parentExpression != null)
            {
                ICollection delegateParameters = parentExpression.GetDelegateParameters();
                if (delegateParameters != null)
                {
                    int parameterCount = delegateParameters.Count;
                    arguments = new List<Expression>(parameterCount);
                    for (int i = 0; i < parameterCount; ++i)
                        arguments.Add(ParameterRef.GetParameterType(delegateParameters, i, parentExpression));
                }
            }
            return arguments;
        }

        protected static bool IsStatic(object obj)
        {
            if (obj is IModifiers)
                return ((IModifiers)obj).IsStatic;
            if (obj is VariableDecl)
                return ((VariableDecl)obj).IsStatic;
            if (obj is MethodDefinition)
                return ((MethodDefinition)obj).IsStatic;
            if (obj is FieldDefinition)
                return ((FieldDefinition)obj).IsStatic;
            if (obj is PropertyDefinition)
                return PropertyDefinitionUtil.IsStatic((PropertyDefinition)obj);
            if (obj is EventDefinition)
                return EventDefintionUtil.IsStatic((EventDefinition)obj);
            if (obj is MethodBase)
                return ((MethodBase)obj).IsStatic;
            if (obj is FieldInfo)
                return ((FieldInfo)obj).IsStatic;
            if (obj is PropertyInfo)
                return PropertyInfoUtil.IsStatic((PropertyInfo)obj);
            if (obj is EventInfo)
                return EventInfoUtil.IsStatic((EventInfo)obj);
            return false;
        }

        /// <summary>
        /// Determine if the target category is only valid at-or-below the MethodDecl level.
        /// </summary>
        public bool IsMethodLevelCategory()
        {
            return ResolveCategoryHelpers.IsMethodLevel[(int)_resolveCategory];
        }

        /// <summary>
        /// Determine if the target category is only valid at-or-below the Type level.
        /// </summary>
        public bool IsTypeLevelCategory()
        {
            return ResolveCategoryHelpers.IsTypeLevel[(int)_resolveCategory];
        }

        /// <summary>
        /// Returns true if there are any matches that aren't internal types imported from other assemblies/projects.
        /// </summary>
        public bool HasMatchesOtherThanImportedInternalTypes()
        {
            foreach (MatchCandidate candidate in _unresolvedRef.Matches)
            {
                object obj = candidate.Object;
                if (obj is TypeDecl)
                {
                    TypeDecl typeDecl = (TypeDecl)obj;
                    if (!typeDecl.IsInternal || typeDecl.FindParent<Project>() == _unresolvedRef.FindParent<Project>())
                        return true;
                }
                else if (obj is TypeDefinition)
                {
                    TypeDefinition typeDefinition = (TypeDefinition)obj;
                    if (!TypeDefinitionUtil.IsInternal(typeDefinition))
                        return true;
                }
                else if (obj is Type)
                {
                    Type type = (Type)obj;
                    if (!TypeUtil.IsInternal(type))
                        return true;
                }
                else
                    return true;
            }
            return false;
        }

        #endregion
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Common Development and Distribution License (CDDL)

Share

About the Author

KenBeckett
Software Developer (Senior)
United States United States
I've been writing software since the late 70's, currently focusing mainly on C#.NET. I also like to travel around the world, and I own a Chocolate Factory (sadly, none of my employees are oompa loompas).

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150129.1 | Last Updated 2 Dec 2012
Article Copyright 2012 by KenBeckett
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid