File: Binder\LookupResultKind.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
#nullable disable
 
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    /// <summary>
    /// Classifies the different ways in which a found symbol might be incorrect.
    /// Higher values are considered "better" than lower values. These values are used
    /// in a few different places:
    ///    1) Inside a LookupResult to indicate the quality of a symbol from lookup.
    ///    2) Inside a bound node (for example, BoundBadExpression), to indicate
    ///       the "binding quality" of the symbols referenced by that bound node.
    ///    3) Inside an error type symbol, to indicate the reason that the candidate symbols
    ///       in the error type symbols were not good.
    ///       
    /// While most of the values can occur in all places, some of the problems are not
    /// detected at lookup time (e.g., NotAVariable), so only occur in bound nodes.
    /// </summary>
    /// <remarks>
    /// This enumeration is parallel to and almost the same as the CandidateReason enumeration.
    /// Changes to one should usually result in changes to the other.
    /// 
    /// There are two enumerations because:
    ///   1) CandidateReason in language-independent, while this enum is language specific.
    ///   2) The name "CandidateReason" didn't make much sense in the way LookupResultKind is used internally.
    ///   3) Viable isn't used in CandidateReason, but we need it in LookupResultKind, and there isn't a 
    ///      a way to have internal enumeration values.
    /// </remarks>
    internal enum LookupResultKind : byte
    {
        // Note: order is important! High values take precedences over lower values. 
 
        Empty,
        NotATypeOrNamespace,
        NotAnAttributeType,
        WrongArity,
        NotCreatable,      // E.g., new of an interface or static class
        Inaccessible,
        NotReferencable,   // E.g., get_Goo binding to an accessor.
        NotAValue,
        NotAVariable,      // used for several slightly different places, e.g. LHS of =, out/ref parameters, etc.
        NotInvocable,
        NotLabel,          // used when a label is required
        StaticInstanceMismatch,
        OverloadResolutionFailure,
 
        // Note: within LookupResult, LookupResultKind.Ambiguous is currently not used (in C#). Instead
        // ambiguous results are determined later by examining multiple viable results to determine if
        // they are ambiguous or overloaded. Thus, LookupResultKind.Ambiguous does not occur in a LookupResult,
        // but can occur within a BoundBadExpression.
        Ambiguous,
 
        // Indicates a set of symbols, and they are totally fine.
        MemberGroup,
 
        // Indicates a single symbol is totally fine.
        Viable,
    }
 
    internal static class LookupResultKindExtensions
    {
        /// <summary>
        /// Maps a LookupResultKind to a CandidateReason. Should not be called on LookupResultKind.Viable!
        /// </summary>
        public static CandidateReason ToCandidateReason(this LookupResultKind resultKind)
        {
            switch (resultKind)
            {
                case LookupResultKind.Empty: return CandidateReason.None;
                case LookupResultKind.NotATypeOrNamespace: return CandidateReason.NotATypeOrNamespace;
                case LookupResultKind.NotAnAttributeType: return CandidateReason.NotAnAttributeType;
                case LookupResultKind.WrongArity: return CandidateReason.WrongArity;
                case LookupResultKind.Inaccessible: return CandidateReason.Inaccessible;
                case LookupResultKind.NotCreatable: return CandidateReason.NotCreatable;
                case LookupResultKind.NotReferencable: return CandidateReason.NotReferencable;
                case LookupResultKind.NotAValue: return CandidateReason.NotAValue;
                case LookupResultKind.NotAVariable: return CandidateReason.NotAVariable;
                case LookupResultKind.NotInvocable: return CandidateReason.NotInvocable;
                case LookupResultKind.StaticInstanceMismatch: return CandidateReason.StaticInstanceMismatch;
                case LookupResultKind.OverloadResolutionFailure: return CandidateReason.OverloadResolutionFailure;
                case LookupResultKind.Ambiguous: return CandidateReason.Ambiguous;
                case LookupResultKind.MemberGroup: return CandidateReason.MemberGroup;
 
                case LookupResultKind.Viable:
                    Debug.Assert(false, "Should not call this on LookupResultKind.Viable");
                    return CandidateReason.None;
 
                default:
                    throw ExceptionUtilities.UnexpectedValue(resultKind);
            }
        }
 
        // Return the lowest non-empty result kind
        public static LookupResultKind WorseResultKind(this LookupResultKind resultKind1, LookupResultKind resultKind2)
        {
            if (resultKind1 == LookupResultKind.Empty)
                return resultKind2;
            if (resultKind2 == LookupResultKind.Empty)
                return resultKind1;
            if (resultKind1 < resultKind2)
                return resultKind1;
            else
                return resultKind2;
        }
    }
}