|
// 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.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class Binder
{
/// <summary>
/// Performs name lookup for simple generic or non-generic name
/// within an optional qualifier namespace or type symbol.
/// If LookupOption.AttributeTypeOnly is set, then it performs
/// attribute type lookup which involves attribute name lookup
/// with and without "Attribute" suffix.
/// </summary>
internal void LookupSymbolsSimpleName(
LookupResult result,
NamespaceOrTypeSymbol qualifierOpt,
string plainName,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (options.IsAttributeTypeLookup())
{
this.LookupAttributeType(result, qualifierOpt, plainName, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
}
else
{
this.LookupSymbolsOrMembersInternal(result, qualifierOpt, plainName, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
}
}
internal void LookupExtensionMethods(LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
foreach (var scope in new ExtensionMethodScopes(this))
{
this.LookupExtensionMethodsInSingleBinder(scope, result, name, arity, options, ref useSiteInfo);
}
}
/// <summary>
/// Look for any symbols in scope with the given name and arity.
/// </summary>
/// <remarks>
/// Makes a second attempt if the results are not viable, in order to produce more detailed failure information (symbols and diagnostics).
/// </remarks>
private Binder LookupSymbolsWithFallback(LookupResult result, string name, int arity, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved = null, LookupOptions options = LookupOptions.Default)
{
Debug.Assert(options.AreValid());
// don't create diagnosis instances unless lookup fails
var binder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: false, useSiteInfo: ref useSiteInfo);
Debug.Assert((binder != null) || result.IsClear);
if (result.Kind != LookupResultKind.Viable && result.Kind != LookupResultKind.Empty)
{
result.Clear();
// retry to get diagnosis
var otherBinder = this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose: true, useSiteInfo: ref useSiteInfo);
Debug.Assert(binder == otherBinder);
}
Debug.Assert(result.IsMultiViable || result.IsClear || result.Error != null);
return binder;
}
private Binder LookupSymbolsInternal(
LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(result.IsClear);
Debug.Assert(options.AreValid());
Binder binder = null;
for (var scope = this; scope != null && !result.IsMultiViable; scope = scope.Next)
{
if (binder != null)
{
var tmp = LookupResult.GetInstance();
scope.LookupSymbolsInSingleBinder(tmp, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteInfo);
result.MergeEqual(tmp);
tmp.Free();
}
else
{
scope.LookupSymbolsInSingleBinder(result, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteInfo);
if (!result.IsClear)
{
binder = scope;
}
}
if ((options & LookupOptions.LabelsOnly) != 0 && scope.IsLastBinderWithinMember())
{
// Labels declared outside of a member are not visible inside.
break;
}
}
return binder;
}
internal virtual void LookupSymbolsInSingleBinder(
LookupResult result, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
}
/// <summary>
/// If qualifierOpt is null, look for any symbols in
/// scope with the given name and arity.
/// Otherwise look for symbols that are members of the specified qualifierOpt.
/// </summary>
private void LookupSymbolsOrMembersInternal(
LookupResult result,
NamespaceOrTypeSymbol qualifierOpt,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if ((object)qualifierOpt == null)
{
this.LookupSymbolsInternal(result, name, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
}
else
{
this.LookupMembersInternal(result, qualifierOpt, name, arity, basesBeingResolved, options, this, diagnose, ref useSiteInfo);
}
}
/// <summary>
/// Look for symbols that are members of the specified namespace or type.
/// </summary>
private void LookupMembersWithFallback(LookupResult result, NamespaceOrTypeSymbol nsOrType, string name, int arity, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved = null, LookupOptions options = LookupOptions.Default)
{
Debug.Assert(options.AreValid());
// don't create diagnosis unless lookup fails
this.LookupMembersInternal(result, nsOrType, name, arity, basesBeingResolved, options, originalBinder: this, diagnose: false, useSiteInfo: ref useSiteInfo);
if (!result.IsMultiViable && !result.IsClear)
{
result.Clear();
// retry to get diagnosis
this.LookupMembersInternal(result, nsOrType, name, arity, basesBeingResolved, options, originalBinder: this, diagnose: true, useSiteInfo: ref useSiteInfo);
}
Debug.Assert(result.IsMultiViable || result.IsClear || result.Error != null);
}
protected void LookupMembersInternal(LookupResult result, NamespaceOrTypeSymbol nsOrType, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(options.AreValid());
Debug.Assert(arity >= 0);
if (nsOrType.IsNamespace)
{
LookupMembersInNamespace(result, (NamespaceSymbol)nsOrType, name, arity, options, originalBinder, diagnose, ref useSiteInfo);
}
else
{
this.LookupMembersInType(result, (TypeSymbol)nsOrType, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
}
}
// Looks up a member of given name and arity in a particular type.
protected void LookupMembersInType(LookupResult result, TypeSymbol type, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
switch (type.TypeKind)
{
case TypeKind.TypeParameter:
this.LookupMembersInTypeParameter(result, (TypeParameterSymbol)type, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
break;
case TypeKind.Interface:
this.LookupMembersInInterface(result, (NamedTypeSymbol)type, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
break;
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Enum:
case TypeKind.Delegate:
case TypeKind.Array:
case TypeKind.Dynamic:
case TypeKind.Submission:
this.LookupMembersInClass(result, type, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
break;
case TypeKind.Error:
LookupMembersInErrorType(result, (ErrorTypeSymbol)type, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
break;
case TypeKind.Pointer:
case TypeKind.FunctionPointer:
result.Clear();
break;
case TypeKind.Unknown:
default:
throw ExceptionUtilities.UnexpectedValue(type.TypeKind);
}
// TODO: Diagnose ambiguity problems here, and conflicts between non-method and method? Or is that
// done in the caller?
}
private void LookupMembersInErrorType(LookupResult result, ErrorTypeSymbol errorType, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (!errorType.CandidateSymbols.IsDefault && errorType.CandidateSymbols.Length == 1)
{
// The dev11 IDE experience provided meaningful information about members of inaccessible types,
// so we should do the same (DevDiv #633340).
// TODO: generalize to other result kinds and/or candidate counts?
if (errorType.ResultKind == LookupResultKind.Inaccessible)
{
TypeSymbol candidateType = errorType.CandidateSymbols.First() as TypeSymbol;
if ((object)candidateType != null)
{
LookupMembersInType(result, candidateType, name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
return; // Bypass call to Clear()
}
}
}
result.Clear();
}
/// <summary>
/// Lookup a member name in a submission chain.
/// </summary>
/// <remarks>
/// We start with the current submission class and walk the submission chain back to the first submission.
/// The search has two phases
/// 1) We are looking for any symbol matching the given name, arity, and options. If we don't find any the search is over.
/// If we find and overloadable symbol(s) (a method or an indexer) we start looking for overloads of this kind
/// (lookingForOverloadsOfKind) of symbol in phase 2.
/// 2) If a visited submission contains a matching member of a kind different from lookingForOverloadsOfKind we stop
/// looking further. Otherwise, if we find viable overload(s) we add them into the result.
///
/// Note that indexers are not supported in script but we deal with them here to handle errors.
/// </remarks>
protected void LookupMembersInSubmissions(LookupResult result, TypeSymbol submissionClass, CompilationUnitSyntax declarationSyntax, bool inUsings, string name, int arity, ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
LookupResult submissionSymbols = LookupResult.GetInstance();
LookupResult nonViable = LookupResult.GetInstance();
SymbolKind? lookingForOverloadsOfKind = null;
bool isSubmissionTree = Compilation.IsSubmissionSyntaxTree(declarationSyntax.SyntaxTree);
// TODO: optimize lookup (there might be many interactions in the chain)
for (CSharpCompilation submission = Compilation; submission != null; submission = submission.PreviousSubmission)
{
submissionSymbols.Clear();
var isCurrentSubmission = submission == Compilation;
var considerUsings = !(isCurrentSubmission && inUsings);
Imports submissionImports;
if (!considerUsings)
{
submissionImports = Imports.Empty;
}
else if (isSubmissionTree)
{
submissionImports = submission.GetSubmissionImports();
}
else if (isCurrentSubmission)
{
submissionImports = ((SourceNamespaceSymbol)Compilation.SourceModule.GlobalNamespace).GetImports(declarationSyntax, basesBeingResolved);
}
else
{
submissionImports = Imports.Empty;
}
// If a viable using alias and a matching member are both defined in the submission an error is reported elsewhere.
// Ignore the member in such case.
if ((options & LookupOptions.NamespaceAliasesOnly) == 0 && (object)submission.ScriptClass != null)
{
LookupMembersWithoutInheritance(submissionSymbols, submission.ScriptClass, name, arity, options, originalBinder, submissionClass, diagnose, ref useSiteInfo, basesBeingResolved);
// NB: It doesn't matter that submissionImports hasn't been expanded since we're not actually using the alias target.
if (submissionSymbols.IsMultiViable &&
considerUsings &&
IsUsingAlias(submissionImports.UsingAliases, name, originalBinder.IsSemanticModelBinder))
{
// using alias is ambiguous with another definition within the same submission iff the other definition is a 0-ary type or a non-type:
Symbol existingDefinition = submissionSymbols.Symbols.First();
if (existingDefinition.Kind != SymbolKind.NamedType || arity == 0)
{
CSDiagnosticInfo diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_ConflictingAliasAndDefinition, name, existingDefinition.GetKindText());
var error = new ExtendedErrorTypeSymbol((NamespaceOrTypeSymbol)null, name, arity, diagInfo, unreported: true);
result.SetFrom(LookupResult.Good(error)); // force lookup to be done w/ error symbol as result
break;
}
}
}
if (!submissionSymbols.IsMultiViable && considerUsings)
{
if (!isCurrentSubmission)
{
submissionImports = Imports.ExpandPreviousSubmissionImports(submissionImports, Compilation);
}
// NB: We diverge from InContainerBinder here and only look in aliases.
// In submissions, regular usings are bubbled up to the outermost scope.
LookupSymbolInAliases(submissionImports.UsingAliases, submissionImports.ExternAliases, originalBinder, submissionSymbols, name, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
}
if (lookingForOverloadsOfKind == null)
{
if (!submissionSymbols.IsMultiViable)
{
// skip non-viable members, but remember them in case no viable members are found in previous submissions:
nonViable.MergePrioritized(submissionSymbols);
continue;
}
result.MergeEqual(submissionSymbols);
Symbol firstSymbol = submissionSymbols.Symbols.First();
if (!IsMethodOrIndexer(firstSymbol))
{
break;
}
// we are now looking for any kind of member regardless of the original binding restrictions:
options = options & ~(LookupOptions.MustBeInvocableIfMember | LookupOptions.NamespacesOrTypesOnly);
lookingForOverloadsOfKind = firstSymbol.Kind;
}
else
{
// found a non-method - the overload set is final now
if (submissionSymbols.Symbols.Count > 0 && submissionSymbols.Symbols.First().Kind != lookingForOverloadsOfKind.Value)
{
break;
}
// found a viable overload:
if (submissionSymbols.IsMultiViable)
{
// merge overloads:
Debug.Assert(result.Symbols.All(IsMethodOrIndexer));
result.MergeEqual(submissionSymbols);
}
}
}
if (result.Symbols.Count == 0)
{
result.SetFrom(nonViable);
}
submissionSymbols.Free();
nonViable.Free();
}
protected bool IsUsingAlias(ImmutableDictionary<string, AliasAndUsingDirective> usingAliases, string name, bool callerIsSemanticModel)
{
AliasAndUsingDirective node;
if (usingAliases.TryGetValue(name, out node))
{
// This method is called by InContainerBinder.LookupSymbolsInSingleBinder to see if
// there's a conflict between an alias and a member. As a conflict may cause a
// speculative lambda binding to fail this is semantically relevant and we need to
// mark this using alias as referenced (and thus not something that can be removed).
MarkImportDirective(node.UsingDirectiveReference, callerIsSemanticModel);
return true;
}
return false;
}
protected void MarkImportDirective(SyntaxReference directive, bool callerIsSemanticModel)
{
if (directive != null && !callerIsSemanticModel)
{
Compilation.MarkImportDirectiveAsUsed(directive);
}
}
protected void LookupSymbolInAliases(
ImmutableDictionary<string, AliasAndUsingDirective> usingAliases,
ImmutableArray<AliasAndExternAliasDirective> externAliases,
Binder originalBinder,
LookupResult result,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
bool callerIsSemanticModel = originalBinder.IsSemanticModelBinder;
AliasAndUsingDirective alias;
if (usingAliases.TryGetValue(name, out alias))
{
// Found a match in our list of normal aliases. Mark the alias as being seen so that
// it won't be reported to the user as something that can be removed.
var res = originalBinder.CheckViability(alias.Alias, arity, options, null, diagnose, ref useSiteInfo, basesBeingResolved);
if (res.Kind == LookupResultKind.Viable)
{
MarkImportDirective(alias.UsingDirectiveReference, callerIsSemanticModel);
}
result.MergeEqual(res);
}
foreach (var a in externAliases)
{
if (!a.SkipInLookup && a.Alias.Name == name)
{
// Found a match in our list of extern aliases. Mark the extern alias as being
// seen so that it won't be reported to the user as something that can be
// removed.
var res = originalBinder.CheckViability(a.Alias, arity, options, null, diagnose, ref useSiteInfo, basesBeingResolved);
if (res.Kind == LookupResultKind.Viable)
{
MarkImportDirective(a.ExternAliasDirectiveReference, callerIsSemanticModel);
}
result.MergeEqual(res);
}
}
}
private static void LookupMembersInNamespace(LookupResult result, NamespaceSymbol ns, string name, int arity, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
var members = GetCandidateMembers(ns, name, options, originalBinder);
foreach (Symbol member in members)
{
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(member, arity, options, null, diagnose, ref useSiteInfo);
result.MergeEqual(resultOfThisMember);
}
}
/// <summary>
/// Lookup extension methods by name and arity in the given binder and
/// check viability in this binder. The lookup is performed on a single
/// binder because extension method search stops at the first applicable
/// method group from the nearest enclosing namespace.
/// </summary>
private void LookupExtensionMethodsInSingleBinder(ExtensionMethodScope scope, LookupResult result, string name, int arity, LookupOptions options, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
var methods = ArrayBuilder<MethodSymbol>.GetInstance();
var binder = scope.Binder;
binder.GetCandidateExtensionMethods(methods, name, arity, options, this);
foreach (var method in methods)
{
SingleLookupResult resultOfThisMember = this.CheckViability(method, arity, options, null, diagnose: true, useSiteInfo: ref useSiteInfo);
result.MergeEqual(resultOfThisMember);
}
methods.Free();
}
#region "AttributeTypeLookup"
/// <summary>
/// Lookup attribute name in the given binder. By default two name lookups are performed:
/// (1) With the provided name
/// (2) With an Attribute suffix added to the provided name
/// Lookup with Attribute suffix is performed only if LookupOptions.VerbatimAttributeName is not set.
///
/// If either lookup is ambiguous, we return the corresponding result with ambiguous symbols.
/// Else if exactly one result is single viable attribute type, we return that result.
/// Otherwise, we return a non-viable result with LookupResult.NotAnAttributeType or an empty result.
/// </summary>
private void LookupAttributeType(
LookupResult result,
NamespaceOrTypeSymbol qualifierOpt,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(result.IsClear);
Debug.Assert(options.AreValid());
Debug.Assert(options.IsAttributeTypeLookup());
// SPEC: By convention, attribute classes are named with a suffix of Attribute.
// SPEC: An attribute-name of the form type-name may either include or omit this suffix.
// SPEC: If an attribute class is found both with and without this suffix, an ambiguity
// SPEC: is present, and a compile-time error results. If the attribute-name is spelled
// SPEC: such that its right-most identifier is a verbatim identifier (§2.4.2), then only
// SPEC: an attribute without a suffix is matched, thus enabling such an ambiguity to be resolved.
// Roslyn Bug 9681: Compilers incorrectly use the *failure* of binding some subexpression to indicate some other strategy is applicable (attributes, 'var')
// Roslyn reproduces Dev10 compiler behavior which doesn't report an error if one of the
// lookups is single viable and other lookup is ambiguous. If one of the lookup results
// (either with or without "Attribute" suffix) is single viable and is an attribute type we
// use it disregarding the second result which may be ambiguous.
// Note: if both are single and attribute types, we still report ambiguity.
// Lookup symbols without attribute suffix.
LookupSymbolsOrMembersInternal(result, qualifierOpt, name, arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
// Result without 'Attribute' suffix added.
Symbol symbolWithoutSuffix;
var attributeTypeWithoutSuffixViabilityUseSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(useSiteInfo);
bool resultWithoutSuffixIsViable = IsSingleViableAttributeType(result, out symbolWithoutSuffix, ref attributeTypeWithoutSuffixViabilityUseSiteInfo);
// Result with 'Attribute' suffix added.
LookupResult resultWithSuffix = null;
Symbol symbolWithSuffix = null;
var attributeTypeWithSuffixViabilityUseSiteInfo = new CompoundUseSiteInfo<AssemblySymbol>(useSiteInfo);
bool resultWithSuffixIsViable = false;
if (!options.IsVerbatimNameAttributeTypeLookup())
{
resultWithSuffix = LookupResult.GetInstance();
this.LookupSymbolsOrMembersInternal(resultWithSuffix, qualifierOpt, name + "Attribute", arity, basesBeingResolved, options, diagnose, ref useSiteInfo);
resultWithSuffixIsViable = IsSingleViableAttributeType(resultWithSuffix, out symbolWithSuffix, ref attributeTypeWithSuffixViabilityUseSiteInfo);
}
if (resultWithoutSuffixIsViable && resultWithSuffixIsViable)
{
// Single viable lookup symbol found both with and without Attribute suffix.
// We merge both results, ambiguity error will be reported later in ResultSymbol,
// no need to adjust useSiteInfo based on attributeTypeWithoutSuffixViabilityUseSiteInfo/attributeTypeWithSuffixViabilityUseSiteInfo.
result.MergeEqual(resultWithSuffix);
}
else if (resultWithoutSuffixIsViable)
{
// single viable lookup symbol only found without Attribute suffix, return result.
useSiteInfo.MergeAndClear(ref attributeTypeWithoutSuffixViabilityUseSiteInfo);
}
else if (resultWithSuffixIsViable)
{
Debug.Assert(resultWithSuffix != null);
// Single viable lookup symbol only found with Attribute suffix, return resultWithSuffix.
result.SetFrom(resultWithSuffix);
useSiteInfo.MergeAndClear(ref attributeTypeWithSuffixViabilityUseSiteInfo);
}
else
{
// Both results are clear, non-viable or ambiguous.
// No need to adjust useSiteInfo based on attributeTypeWithoutSuffixViabilityUseSiteInfo/attributeTypeWithSuffixViabilityUseSiteInfo.
if (!result.IsClear)
{
if ((object)symbolWithoutSuffix != null) // was not ambiguous, but not viable
{
result.SetFrom(GenerateNonViableAttributeTypeResult(symbolWithoutSuffix, result.Error, diagnose));
}
}
if (resultWithSuffix != null)
{
if (!resultWithSuffix.IsClear)
{
if ((object)symbolWithSuffix != null)
{
resultWithSuffix.SetFrom(GenerateNonViableAttributeTypeResult(symbolWithSuffix, resultWithSuffix.Error, diagnose));
}
}
result.MergePrioritized(resultWithSuffix);
}
}
resultWithSuffix?.Free();
}
private bool IsAmbiguousResult(LookupResult result, out Symbol resultSymbol)
{
resultSymbol = null;
var symbols = result.Symbols;
switch (symbols.Count)
{
case 0:
return false;
case 1:
resultSymbol = symbols[0];
return false;
default:
// If there are two or more symbols in the result, one from source and others from PE,
// then the source symbol overrides the PE symbols and must be chosen.
// NOTE: Kind of the symbol doesn't matter here. If the resolved symbol is not an attribute type, we will subsequently generate a lookup error.
// CONSIDER: If this source symbol is the eventual result symbol for attribute type lookup and it is not a valid attribute type,
// CONSIDER: we generate an error but don't generate warning CS0436 for source/PE name conflict.
// CONSIDER: We may want to also generate CS0436 for this case.
resultSymbol = ResolveMultipleSymbolsInAttributeTypeLookup(symbols);
return (object)resultSymbol == null;
}
}
private Symbol ResolveMultipleSymbolsInAttributeTypeLookup(ArrayBuilder<Symbol> symbols)
{
Debug.Assert(symbols.Count >= 2);
var originalSymbols = symbols.ToImmutable();
for (int i = 0; i < symbols.Count; i++)
{
symbols[i] = UnwrapAliasNoDiagnostics(symbols[i]);
}
BestSymbolInfo secondBest;
BestSymbolInfo best = GetBestSymbolInfo(symbols, out secondBest);
Debug.Assert(!best.IsNone);
Debug.Assert(!secondBest.IsNone);
if (best.IsFromCompilation && !secondBest.IsFromCompilation)
{
var srcSymbol = symbols[best.Index];
var mdSymbol = symbols[secondBest.Index];
//if names match, arities match, and containing symbols match (recursively), ...
if (NameAndArityMatchRecursively(srcSymbol, mdSymbol))
{
return originalSymbols[best.Index];
}
}
return null;
}
private static bool NameAndArityMatchRecursively(Symbol x, Symbol y)
{
while (true)
{
if (isRoot(x))
{
return isRoot(y);
}
if (isRoot(y))
{
return false;
}
if (x.Name != y.Name || x.GetArity() != y.GetArity())
{
return false;
}
x = x.ContainingSymbol;
y = y.ContainingSymbol;
}
static bool isRoot(Symbol symbol) => symbol is null || symbol is NamespaceSymbol { IsGlobalNamespace: true };
}
private bool IsSingleViableAttributeType(LookupResult result, out Symbol symbol, ref CompoundUseSiteInfo<AssemblySymbol> attributeTypeViabilityUseSiteInfo)
{
if (IsAmbiguousResult(result, out symbol))
{
return false;
}
if (result == null || result.Kind != LookupResultKind.Viable || (object)symbol == null)
{
return false;
}
DiagnosticInfo discarded = null;
return CheckAttributeTypeViability(UnwrapAliasNoDiagnostics(symbol), diagnose: false, diagInfo: ref discarded, ref attributeTypeViabilityUseSiteInfo);
}
private SingleLookupResult GenerateNonViableAttributeTypeResult(Symbol symbol, DiagnosticInfo diagInfo, bool diagnose)
{
Debug.Assert((object)symbol != null);
symbol = UnwrapAliasNoDiagnostics(symbol);
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
CheckAttributeTypeViability(symbol, diagnose, ref diagInfo, ref discardedUseSiteInfo);
return LookupResult.NotAnAttributeType(symbol, diagInfo);
}
private bool CheckAttributeTypeViability(Symbol symbol, bool diagnose, ref DiagnosticInfo diagInfo, ref CompoundUseSiteInfo<AssemblySymbol> attributeTypeViabilityUseSiteInfo)
{
Debug.Assert((object)symbol != null);
if (symbol.Kind == SymbolKind.NamedType)
{
var namedType = (NamedTypeSymbol)symbol;
if (namedType.IsAbstract)
{
// Attribute class cannot be abstract.
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_AbstractAttributeClass, symbol) : null;
return false;
}
else
{
var useSiteInfo = attributeTypeViabilityUseSiteInfo.AccumulatesDependencies || !diagnose ?
new CompoundUseSiteInfo<AssemblySymbol>(attributeTypeViabilityUseSiteInfo) :
CompoundUseSiteInfo<AssemblySymbol>.DiscardedDependencies;
Debug.Assert(!diagnose || useSiteInfo.AccumulatesDiagnostics);
if (Compilation.IsEqualOrDerivedFromWellKnownClass(namedType, WellKnownType.System_Attribute, ref useSiteInfo))
{
attributeTypeViabilityUseSiteInfo.MergeAndClear(ref useSiteInfo);
// Reuse existing diagnostic info.
return true;
}
if (diagnose && useSiteInfo.HasErrors)
{
foreach (var info in useSiteInfo.Diagnostics)
{
if (info.Severity == DiagnosticSeverity.Error)
{
diagInfo = info;
return false;
}
}
}
}
}
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_NotAnAttributeClass, symbol) : null;
return false;
}
#endregion
internal virtual bool SupportsExtensionMethods
{
get { return false; }
}
/// <summary>
/// Return the extension methods from this specific binding scope that match the name and optional
/// arity. Since the lookup of extension methods is iterative, proceeding one binding scope at a time,
/// GetCandidateExtensionMethods should not defer to the next binding scope. Instead, the caller is
/// responsible for walking the nested binding scopes from innermost to outermost. This method is overridden
/// to search the available members list in binding types that represent types, namespaces, and usings.
/// </summary>
internal virtual void GetCandidateExtensionMethods(
ArrayBuilder<MethodSymbol> methods,
string name,
int arity,
LookupOptions options,
Binder originalBinder)
{
}
// Does a member lookup in a single type, without considering inheritance.
protected static void LookupMembersWithoutInheritance(LookupResult result, TypeSymbol type, string name, int arity,
LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved)
{
var members = GetCandidateMembers(type, name, options, originalBinder);
foreach (Symbol member in members)
{
// Do we need to exclude override members, or is that done later by overload resolution. It seems like
// not excluding them here can't lead to problems, because we will always find the overridden method as well.
SingleLookupResult resultOfThisMember = originalBinder.CheckViability(member, arity, options, accessThroughType, diagnose, ref useSiteInfo, basesBeingResolved);
result.MergeEqual(resultOfThisMember);
}
}
// Lookup member in a class, struct, enum, delegate.
private void LookupMembersInClass(
LookupResult result,
TypeSymbol type,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
Binder originalBinder,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
LookupMembersInClass(result, type, name, arity, basesBeingResolved, options, originalBinder, type, diagnose, ref useSiteInfo);
}
// Lookup member in a class, struct, enum, delegate.
private void LookupMembersInClass(
LookupResult result,
TypeSymbol type,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
Binder originalBinder,
TypeSymbol accessThroughType,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert((object)type != null);
Debug.Assert(!type.IsInterfaceType() && type.TypeKind != TypeKind.TypeParameter);
TypeSymbol currentType = type;
var tmp = LookupResult.GetInstance();
PooledHashSet<NamedTypeSymbol> visited = null;
while ((object)currentType != null)
{
tmp.Clear();
LookupMembersWithoutInheritance(tmp, currentType, name, arity, options, originalBinder, accessThroughType, diagnose, ref useSiteInfo, basesBeingResolved);
MergeHidingLookupResults(result, tmp, basesBeingResolved, ref useSiteInfo);
// If the type is from a winmd and implements any of the special WinRT collection
// projections then we may need to add underlying interface members.
NamedTypeSymbol namedType = currentType as NamedTypeSymbol;
if (namedType?.ShouldAddWinRTMembers == true)
{
AddWinRTMembers(result, namedType, name, arity, options, originalBinder, diagnose, ref useSiteInfo);
}
// any viable non-methods [non-indexers] found here will hide viable methods [indexers] (with the same name) in any further base classes
bool tmpHidesMethodOrIndexers = tmp.IsMultiViable && !IsMethodOrIndexer(tmp.Symbols[0]);
// short circuit looking up bases if we already have a viable result and we won't be adding on more
if (result.IsMultiViable && (tmpHidesMethodOrIndexers || !IsMethodOrIndexer(result.Symbols[0])))
{
break;
}
if (basesBeingResolved != null && basesBeingResolved.ContainsReference(type.OriginalDefinition))
{
var other = GetNearestOtherSymbol(basesBeingResolved, type);
var diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_CircularBase, type, other);
var error = new ExtendedErrorTypeSymbol(this.Compilation, name, arity, diagInfo, unreported: true);
result.SetFrom(LookupResult.Good(error)); // force lookup to be done w/ error symbol as result
}
// As in dev11, we don't consider inherited members within crefs.
// CAVEAT: dev11 appears to ignore this rule within parameter types and return types,
// so we're checking Cref, rather than Cref and CrefParameterOrReturnType.
if (originalBinder.InCrefButNotParameterOrReturnType)
{
break;
}
currentType = currentType.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, this.Compilation, ref visited);
if ((object)currentType != null)
{
currentType.OriginalDefinition.AddUseSiteInfo(ref useSiteInfo);
}
}
visited?.Free();
tmp.Free();
}
/// <summary>
/// If the type implements one of a select few WinRT interfaces, the interface type is
/// projected to the CLR collection type (e.g., IVector to IList).
/// When importing a winmd type it may implement one or more winmd collection
/// interfaces. When the collection interfaces are projected, we may need
/// to add the projected members to the imported type so that calls to those
/// members succeed as normal. This method adds the interface methods to
/// the lookup, if necessary. The CLR understands that a call to the .NET interface
/// should be projected onto the WinRT interface method.
/// </summary>
private void AddWinRTMembers(
LookupResult result,
NamedTypeSymbol type,
string name,
int arity,
LookupOptions options,
Binder originalBinder,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// While the fundamental idea is simple, the implementation has issues.
// If we have no conflict with existing members, we also have to check
// if we have a conflict with other interface members. An example would be
// a type which implements both IIterable (IEnumerable) and IMap
// (IDictionary).There are two different GetEnumerator methods from each
// interface. Thus, we don't know which method to choose. The solution?
// Don't add any GetEnumerator method.
var comparer = MemberSignatureComparer.CSharpOverrideComparer;
var allMembers = new HashSet<Symbol>(comparer);
var conflictingMembers = new HashSet<Symbol>(comparer);
// Add all viable members from type lookup
if (result.IsMultiViable)
{
foreach (var sym in result.Symbols)
{
// Fields can't be present in the HashSet because they can't be compared
// with a MemberSignatureComparer
if (sym.Kind == SymbolKind.Method || sym.Kind == SymbolKind.Property)
{
allMembers.Add(sym);
}
}
}
var tmp = LookupResult.GetInstance();
NamedTypeSymbol idictSymbol, iroDictSymbol, iListSymbol, iCollectionSymbol, inccSymbol, inpcSymbol;
GetWellKnownWinRTMemberInterfaces(out idictSymbol, out iroDictSymbol, out iListSymbol, out iCollectionSymbol, out inccSymbol, out inpcSymbol);
// Dev11 searches all declared and undeclared base interfaces
foreach (var iface in type.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo))
{
if (ShouldAddWinRTMembersForInterface(iface, idictSymbol, iroDictSymbol, iListSymbol, iCollectionSymbol, inccSymbol, inpcSymbol))
{
LookupMembersWithoutInheritance(tmp, iface, name, arity, options, originalBinder, iface, diagnose, ref useSiteInfo, basesBeingResolved: null);
// only add viable members
if (tmp.IsMultiViable)
{
foreach (var sym in tmp.Symbols)
{
if (!allMembers.Add(sym))
{
conflictingMembers.Add(sym);
}
}
}
tmp.Clear();
}
}
tmp.Free();
if (result.IsMultiViable)
{
foreach (var sym in result.Symbols)
{
if (sym.Kind == SymbolKind.Method || sym.Kind == SymbolKind.Property)
{
allMembers.Remove(sym);
conflictingMembers.Remove(sym);
}
}
}
foreach (var sym in allMembers)
{
if (!conflictingMembers.Contains(sym))
{
// since we only added viable members, every lookupresult should be viable
result.MergeEqual(new SingleLookupResult(LookupResultKind.Viable, sym, null));
}
}
}
private void GetWellKnownWinRTMemberInterfaces(out NamedTypeSymbol idictSymbol, out NamedTypeSymbol iroDictSymbol, out NamedTypeSymbol iListSymbol, out NamedTypeSymbol iCollectionSymbol, out NamedTypeSymbol inccSymbol, out NamedTypeSymbol inpcSymbol)
{
idictSymbol = Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IDictionary_KV);
iroDictSymbol = Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_IReadOnlyDictionary_KV);
iListSymbol = Compilation.GetWellKnownType(WellKnownType.System_Collections_IList);
iCollectionSymbol = Compilation.GetWellKnownType(WellKnownType.System_Collections_ICollection);
inccSymbol = Compilation.GetWellKnownType(WellKnownType.System_Collections_Specialized_INotifyCollectionChanged);
inpcSymbol = Compilation.GetWellKnownType(WellKnownType.System_ComponentModel_INotifyPropertyChanged);
}
private static bool ShouldAddWinRTMembersForInterface(NamedTypeSymbol iface, NamedTypeSymbol idictSymbol, NamedTypeSymbol iroDictSymbol, NamedTypeSymbol iListSymbol, NamedTypeSymbol iCollectionSymbol, NamedTypeSymbol inccSymbol, NamedTypeSymbol inpcSymbol)
{
var iFaceOriginal = iface.OriginalDefinition;
var iFaceSpecial = iFaceOriginal.SpecialType;
// Types match the list given in dev11 IMPORTER::GetWindowsRuntimeInterfacesToFake
return iFaceSpecial == SpecialType.System_Collections_Generic_IEnumerable_T ||
iFaceSpecial == SpecialType.System_Collections_Generic_IList_T ||
iFaceSpecial == SpecialType.System_Collections_Generic_ICollection_T ||
TypeSymbol.Equals(iFaceOriginal, idictSymbol, TypeCompareKind.ConsiderEverything2) ||
iFaceSpecial == SpecialType.System_Collections_Generic_IReadOnlyList_T ||
iFaceSpecial == SpecialType.System_Collections_Generic_IReadOnlyCollection_T ||
TypeSymbol.Equals(iFaceOriginal, iroDictSymbol, TypeCompareKind.ConsiderEverything2) ||
iFaceSpecial == SpecialType.System_Collections_IEnumerable ||
TypeSymbol.Equals(iFaceOriginal, iListSymbol, TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(iFaceOriginal, iCollectionSymbol, TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(iFaceOriginal, inccSymbol, TypeCompareKind.ConsiderEverything2) ||
TypeSymbol.Equals(iFaceOriginal, inpcSymbol, TypeCompareKind.ConsiderEverything2);
}
// find the nearest symbol in list to the symbol 'type'. It may be the same symbol if its the only one.
private static Symbol GetNearestOtherSymbol(ConsList<TypeSymbol> list, TypeSymbol type)
{
TypeSymbol other = type;
for (; list != null && list != ConsList<TypeSymbol>.Empty; list = list.Tail)
{
if (TypeSymbol.Equals(list.Head, type.OriginalDefinition, TypeCompareKind.ConsiderEverything2))
{
if (TypeSymbol.Equals(other, type, TypeCompareKind.ConsiderEverything2) && list.Tail != null && list.Tail != ConsList<TypeSymbol>.Empty)
{
other = list.Tail.Head;
}
break;
}
else
{
other = list.Head;
}
}
return other;
}
// Lookup member in interface, and any base interfaces.
private void LookupMembersInInterfaceOnly(
LookupResult current,
NamedTypeSymbol type,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
Binder originalBinder,
TypeSymbol accessThroughType,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert((object)type != null);
Debug.Assert(type.IsInterface);
LookupMembersWithoutInheritance(current, type, name, arity, options, originalBinder, accessThroughType, diagnose, ref useSiteInfo, basesBeingResolved);
if ((options & LookupOptions.NamespaceAliasesOnly) == 0 && !originalBinder.InCrefButNotParameterOrReturnType &&
((options & LookupOptions.NamespacesOrTypesOnly) == 0 || !(current.IsSingleViable && TypeSymbol.Equals(current.SingleSymbolOrDefault.ContainingType, type, TypeCompareKind.AllIgnoreOptions)))) // The nested type will shadow everything from bases
{
LookupMembersInInterfacesWithoutInheritance(current, GetBaseInterfaces(type, basesBeingResolved, ref useSiteInfo),
name, arity, basesBeingResolved, options, originalBinder, accessThroughType, diagnose, ref useSiteInfo);
}
}
private static ImmutableArray<NamedTypeSymbol> GetBaseInterfaces(NamedTypeSymbol type, ConsList<TypeSymbol> basesBeingResolved, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (basesBeingResolved?.Any() != true)
{
return type.AllInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);
}
if (basesBeingResolved.ContainsReference(type.OriginalDefinition))
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
var interfaces = type.GetDeclaredInterfaces(basesBeingResolved);
if (interfaces.IsEmpty)
{
return ImmutableArray<NamedTypeSymbol>.Empty;
}
// The C# language specification forbids an interface from extending another interface that depends on it.
// That prevents a legal program from having an infinite inheritance chain. We protect most of the
// compiler from having to deal with infinite inheritance chains arising in such illegal programs by
// removing the dependent base interfaces from the interface list. This is accomplished by calling
// `BaseTypeAnalysis.TypeDependsOn` in `SourceNamedTypeSymbol.MakeAcyclicInterfaces`. With the addition
// of support for types nested within interfaces in C# 8.0, type lookup within an interface needs to
// traverse base interfaces. We cannot depend on the interfaces having been previously bound and cleaned
// of these cycles because such cycles might arise while we are binding a base clause. The following
// code is specifically used in that case: to get the list of base interfaces for use in name lookup
// while some base clause is being bound. To prevent infinite recursion in the case of (erroneous)
// infinite inheritance, we stop enumerating base interfaces when we encounter an interface type that
// inherits from an instantiation of the same interface. We therefore track, in `cycleGuard`, the
// current set of interfaces whose base interfaces we are enumerating.
var cycleGuard = ConsList<NamedTypeSymbol>.Empty.Prepend(type.OriginalDefinition);
// Consumers of the result depend on the sorting performed by AllInterfacesWithDefinitionUseSiteDiagnostics.
// Let's use similar sort algorithm.
var result = ArrayBuilder<NamedTypeSymbol>.GetInstance();
var visited = new HashSet<NamedTypeSymbol>(Symbols.SymbolEqualityComparer.ConsiderEverything);
for (int i = interfaces.Length - 1; i >= 0; i--)
{
addAllInterfaces(interfaces[i], visited, result, basesBeingResolved, cycleGuard);
}
result.ReverseContents();
foreach (var candidate in result)
{
candidate.OriginalDefinition.AddUseSiteInfo(ref useSiteInfo);
}
return result.ToImmutableAndFree();
static void addAllInterfaces(NamedTypeSymbol @interface, HashSet<NamedTypeSymbol> visited, ArrayBuilder<NamedTypeSymbol> result, ConsList<TypeSymbol> basesBeingResolved, ConsList<NamedTypeSymbol> cycleGuard)
{
NamedTypeSymbol originalDefinition;
if (@interface.IsInterface && !cycleGuard.ContainsReference(originalDefinition = @interface.OriginalDefinition) && visited.Add(@interface))
{
if (!basesBeingResolved.ContainsReference(originalDefinition))
{
ImmutableArray<NamedTypeSymbol> baseInterfaces = @interface.GetDeclaredInterfaces(basesBeingResolved);
if (!baseInterfaces.IsEmpty)
{
cycleGuard = cycleGuard.Prepend(originalDefinition);
for (int i = baseInterfaces.Length - 1; i >= 0; i--)
{
var baseInterface = baseInterfaces[i];
addAllInterfaces(baseInterface, visited, result, basesBeingResolved, cycleGuard);
}
}
}
result.Add(@interface);
}
}
}
private void LookupMembersInInterfacesWithoutInheritance(
LookupResult current,
ImmutableArray<NamedTypeSymbol> interfaces,
string name,
int arity,
ConsList<TypeSymbol> basesBeingResolved,
LookupOptions options,
Binder originalBinder,
TypeSymbol accessThroughType,
bool diagnose,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (interfaces.Length > 0)
{
var tmp = LookupResult.GetInstance();
HashSet<NamedTypeSymbol> seenInterfaces = null;
if (interfaces.Length > 1)
{
seenInterfaces = new HashSet<NamedTypeSymbol>(Symbols.SymbolEqualityComparer.IgnoringNullable);
}
foreach (NamedTypeSymbol baseInterface in interfaces)
{
if (seenInterfaces is null || seenInterfaces.Add(baseInterface))
{
LookupMembersWithoutInheritance(tmp, baseInterface, name, arity, options, originalBinder, accessThroughType, diagnose, ref useSiteInfo, basesBeingResolved);
MergeHidingLookupResults(current, tmp, basesBeingResolved, ref useSiteInfo);
tmp.Clear();
}
}
tmp.Free();
}
}
// Lookup member in interface, and any base interfaces, and System.Object.
private void LookupMembersInInterface(LookupResult current, NamedTypeSymbol type, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert((object)type != null);
Debug.Assert(type.IsInterface);
LookupMembersInInterfaceOnly(current, type, name, arity, basesBeingResolved, options, originalBinder, type, diagnose, ref useSiteInfo);
if (!originalBinder.InCrefButNotParameterOrReturnType)
{
var tmp = LookupResult.GetInstance();
// NB: we assume use-site-errors on System.Object, if any, have been reported earlier.
this.LookupMembersInClass(tmp, this.Compilation.GetSpecialType(SpecialType.System_Object), name, arity, basesBeingResolved, options, originalBinder, type, diagnose, ref useSiteInfo);
MergeHidingLookupResults(current, tmp, basesBeingResolved, ref useSiteInfo);
tmp.Free();
}
}
// Lookup member in type parameter
private void LookupMembersInTypeParameter(LookupResult current, TypeParameterSymbol typeParameter, string name, int arity, ConsList<TypeSymbol> basesBeingResolved, LookupOptions options, Binder originalBinder, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert((object)typeParameter != null);
if ((options & (LookupOptions.NamespaceAliasesOnly | LookupOptions.NamespacesOrTypesOnly)) != 0)
{
return;
}
// If this ever happens, just return immediately since cref lookup ignores inherited members.
Debug.Assert(!originalBinder.InCref, "Can't dot into type parameters, so how can this happen?");
// The result is the accessible members from the effective base class and
// effective interfaces. AllEffectiveInterfaces is used rather than AllInterfaces
// to avoid including explicit implementations from the effective base class.
LookupMembersInClass(current, typeParameter.EffectiveBaseClass(ref useSiteInfo), name, arity, basesBeingResolved, options, originalBinder, diagnose, ref useSiteInfo);
LookupMembersInInterfacesWithoutInheritance(current, typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo), name, arity, basesBeingResolved: null, options, originalBinder, typeParameter, diagnose, ref useSiteInfo);
}
private static bool IsDerivedType(NamedTypeSymbol baseType, NamedTypeSymbol derivedType, ConsList<TypeSymbol> basesBeingResolved, CSharpCompilation compilation, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(!TypeSymbol.Equals(baseType, derivedType, TypeCompareKind.ConsiderEverything2));
if (basesBeingResolved?.Any() != true)
{
for (NamedTypeSymbol b = derivedType.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo); (object)b != null; b = b.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo))
{
if (TypeSymbol.Equals(b, baseType, TypeCompareKind.ConsiderEverything2)) return true;
}
}
else
{
PooledHashSet<NamedTypeSymbol> visited = null;
for (var b = (NamedTypeSymbol)derivedType.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited);
(object)b != null;
b = (NamedTypeSymbol)b.GetNextBaseTypeNoUseSiteDiagnostics(basesBeingResolved, compilation, ref visited))
{
b.OriginalDefinition.AddUseSiteInfo(ref useSiteInfo);
if (TypeSymbol.Equals(b, baseType, TypeCompareKind.ConsiderEverything2))
{
visited?.Free();
return true;
}
}
visited?.Free();
}
return baseType.IsInterface && GetBaseInterfaces(derivedType, basesBeingResolved, ref useSiteInfo).Contains(baseType);
}
// Merge resultHidden into resultHiding, whereby viable results in resultHiding should hide results
// in resultHidden if the owner of the symbol in resultHiding is a subtype of the owner of the symbol
// in resultHidden. We merge together methods [indexers], but non-methods [non-indexers] hide everything and methods [indexers] hide non-methods [non-indexers].
private void MergeHidingLookupResults(LookupResult resultHiding, LookupResult resultHidden, ConsList<TypeSymbol> basesBeingResolved, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// Methods hide non-methods, non-methods hide everything.
if (resultHiding.IsMultiViable && resultHidden.IsMultiViable)
{
// Check if resultHiding has any non-methods. If so, it hides everything in resultHidden.
var hidingSymbols = resultHiding.Symbols;
var hidingCount = hidingSymbols.Count;
var hiddenSymbols = resultHidden.Symbols;
var hiddenCount = hiddenSymbols.Count;
for (int i = 0; i < hiddenCount; i++)
{
var sym = hiddenSymbols[i];
var hiddenContainer = sym.ContainingType;
// see if sym is hidden
for (int j = 0; j < hidingCount; j++)
{
var hidingSym = hidingSymbols[j];
var hidingContainer = hidingSym.ContainingType;
var hidingContainerIsInterface = hidingContainer.IsInterface;
if (hidingContainerIsInterface)
{
// SPEC: For the purposes of member lookup [...] if T is an
// SPEC: interface type, the base types of T are the base interfaces
// SPEC: of T and the class type object.
if (!IsDerivedType(baseType: hiddenContainer, derivedType: hidingSym.ContainingType, basesBeingResolved, this.Compilation, useSiteInfo: ref useSiteInfo) &&
hiddenContainer.SpecialType != SpecialType.System_Object)
{
continue; // not in inheritance relationship, so it cannot hide
}
}
if (!IsMethodOrIndexer(hidingSym) || !IsMethodOrIndexer(sym))
{
// any non-method [non-indexer] hides everything in the hiding scope
// any method [indexer] hides non-methods [non-indexers].
goto symIsHidden;
}
// Note: We do not implement hiding by signature in non-interfaces here; that is handled later in overload lookup.
}
hidingSymbols.Add(sym); // not hidden
symIsHidden:;
}
}
else
{
resultHiding.MergePrioritized(resultHidden);
}
}
/// <summary>
/// This helper is used to determine whether this symbol hides / is hidden
/// based on its signature, as opposed to its name.
/// </summary>
/// <remarks>
/// CONSIDER: It might be nice to generalize this - maybe an extension method
/// on Symbol (e.g. IsOverloadable or HidesByName).
/// </remarks>
private static bool IsMethodOrIndexer(Symbol symbol)
{
return symbol.Kind == SymbolKind.Method || symbol.IsIndexer();
}
internal static ImmutableArray<Symbol> GetCandidateMembers(NamespaceOrTypeSymbol nsOrType, string name, LookupOptions options, Binder originalBinder)
{
if ((options & LookupOptions.NamespacesOrTypesOnly) != 0 && nsOrType is TypeSymbol)
{
return nsOrType.GetTypeMembers(name).Cast<NamedTypeSymbol, Symbol>();
}
else if (nsOrType.Kind == SymbolKind.NamedType && originalBinder.IsEarlyAttributeBinder)
{
return ((NamedTypeSymbol)nsOrType).GetEarlyAttributeDecodingMembers(name);
}
else if ((options & LookupOptions.LabelsOnly) != 0)
{
return ImmutableArray<Symbol>.Empty;
}
else if (nsOrType is SourceMemberContainerTypeSymbol { HasPrimaryConstructor: true } sourceMemberContainerTypeSymbol)
{
return sourceMemberContainerTypeSymbol.GetCandidateMembersForLookup(name);
}
else
{
return nsOrType.GetMembers(name);
}
}
internal static ImmutableArray<Symbol> GetCandidateMembers(NamespaceOrTypeSymbol nsOrType, LookupOptions options, Binder originalBinder)
{
if ((options & LookupOptions.NamespacesOrTypesOnly) != 0 && nsOrType is TypeSymbol)
{
return StaticCast<Symbol>.From(nsOrType.GetTypeMembersUnordered());
}
else if (nsOrType.Kind == SymbolKind.NamedType && originalBinder.IsEarlyAttributeBinder)
{
return ((NamedTypeSymbol)nsOrType).GetEarlyAttributeDecodingMembers();
}
else if ((options & LookupOptions.LabelsOnly) != 0)
{
return ImmutableArray<Symbol>.Empty;
}
else
{
return nsOrType.GetMembersUnordered();
}
}
private bool IsInScopeOfAssociatedSyntaxTree(Symbol symbol)
{
while (symbol is not null and not NamedTypeSymbol { IsFileLocal: true })
{
symbol = symbol.ContainingType;
}
if (symbol is null)
{
// the passed-in symbol was not contained in a file-local type.
return true;
}
if ((object)symbol.DeclaringCompilation != this.Compilation
&& (this.Flags & BinderFlags.InEEMethodBinder) == 0)
{
return false;
}
var symbolFileIdentifier = ((NamedTypeSymbol)symbol).AssociatedFileIdentifier;
if (symbolFileIdentifier is null || symbolFileIdentifier.FilePathChecksumOpt.IsDefault)
{
// the containing file of the file-local type has an ill-formed path.
return false;
}
var binderFileIdentifier = getFileIdentifierForFileTypes();
return !binderFileIdentifier.FilePathChecksumOpt.IsDefault
&& binderFileIdentifier.FilePathChecksumOpt.SequenceEqual(symbolFileIdentifier.FilePathChecksumOpt);
FileIdentifier getFileIdentifierForFileTypes()
{
for (var binder = this; binder != null; binder = binder.Next)
{
if (binder is BuckStopsHereBinder lastBinder)
{
// we never expect to bind a file type in a context where the BuckStopsHereBinder lacks an AssociatedFileIdentifier
return lastBinder.AssociatedFileIdentifier ?? throw ExceptionUtilities.Unreachable();
}
}
throw ExceptionUtilities.Unreachable();
}
}
/// <remarks>
/// Distinguish from <see cref="CanAddLookupSymbolInfo"/>, which performs an analogous task for Add*LookupSymbolsInfo*.
/// </remarks>
internal SingleLookupResult CheckViability(Symbol symbol, int arity, LookupOptions options, TypeSymbol accessThroughType, bool diagnose, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved = null)
{
Debug.Assert((options & LookupOptions.MustBeAbstractOrVirtual) == 0 || (options & LookupOptions.MustNotBeInstance) != 0);
bool inaccessibleViaQualifier;
DiagnosticInfo diagInfo;
// General pattern: checks and diagnostics refer to unwrapped symbol,
// but lookup results refer to symbol.
var unwrappedSymbol = symbol.Kind == SymbolKind.Alias
? ((AliasSymbol)symbol).GetAliasTarget(basesBeingResolved)
: symbol;
if ((options & LookupOptions.MustNotBeParameter) != 0 && unwrappedSymbol is ParameterSymbol)
{
return LookupResult.Empty();
}
else if (!IsInScopeOfAssociatedSyntaxTree(unwrappedSymbol))
{
return LookupResult.Empty();
}
// Check for symbols marked with 'Microsoft.CodeAnalysis.Embedded' attribute
else if (!this.Compilation.SourceModule.Equals(unwrappedSymbol.ContainingModule) && unwrappedSymbol.IsHiddenByCodeAnalysisEmbeddedAttribute())
{
return LookupResult.Empty();
}
else if ((options & (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstractOrVirtual)) == (LookupOptions.MustNotBeInstance | LookupOptions.MustBeAbstractOrVirtual) &&
(unwrappedSymbol is not TypeSymbol && IsInstance(unwrappedSymbol) || !(unwrappedSymbol.IsAbstract || unwrappedSymbol.IsVirtual)))
{
return LookupResult.Empty();
}
else if (WrongArity(symbol, arity, diagnose, options, out diagInfo))
{
return LookupResult.WrongArity(symbol, diagInfo);
}
else if (!InCref && !unwrappedSymbol.CanBeReferencedByNameIgnoringIllegalCharacters)
{
// Strictly speaking, this test should actually check CanBeReferencedByName.
// However, we don't want to pay that cost in cases where the lookup is based
// on a provided name. As a result, we skip the character check here and let
// SemanticModel.LookupNames filter out invalid names before returning.
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_CantCallSpecialMethod, unwrappedSymbol) : null;
return LookupResult.NotReferencable(symbol, diagInfo);
}
else if ((options & LookupOptions.NamespacesOrTypesOnly) != 0 && !(unwrappedSymbol is NamespaceOrTypeSymbol))
{
return LookupResult.NotTypeOrNamespace(unwrappedSymbol, symbol, diagnose);
}
else if ((options & LookupOptions.MustBeInvocableIfMember) != 0
&& IsNonInvocableMember(unwrappedSymbol))
{
return LookupResult.NotInvocable(unwrappedSymbol, symbol, diagnose);
}
else if (InCref && !this.IsCrefAccessible(unwrappedSymbol))
{
var unwrappedSymbols = ImmutableArray.Create<Symbol>(unwrappedSymbol);
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_BadAccess, new[] { unwrappedSymbol }, unwrappedSymbols, additionalLocations: ImmutableArray<Location>.Empty) : null;
return LookupResult.Inaccessible(symbol, diagInfo);
}
else if (!InCref &&
!this.IsAccessible(unwrappedSymbol,
RefineAccessThroughType(options, accessThroughType),
out inaccessibleViaQualifier,
ref useSiteInfo,
basesBeingResolved))
{
if (!diagnose)
{
diagInfo = null;
}
else if (inaccessibleViaQualifier)
{
diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_BadProtectedAccess, unwrappedSymbol, accessThroughType, this.ContainingType);
}
else if (IsBadIvtSpecification())
{
diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_FriendRefNotEqualToThis, unwrappedSymbol.ContainingAssembly.Identity.ToString(), AssemblyIdentity.PublicKeyToString(this.Compilation.Assembly.PublicKey));
}
else
{
diagInfo = new CSDiagnosticInfo(ErrorCode.ERR_BadAccess, new[] { unwrappedSymbol }, ImmutableArray.Create<Symbol>(unwrappedSymbol), additionalLocations: ImmutableArray<Location>.Empty);
}
return LookupResult.Inaccessible(symbol, diagInfo);
}
else if (!InCref && unwrappedSymbol.MustCallMethodsDirectly())
{
diagInfo = diagnose ? MakeCallMethodsDirectlyDiagnostic(unwrappedSymbol) : null;
return LookupResult.NotReferencable(symbol, diagInfo);
}
else if ((options & LookupOptions.MustBeInstance) != 0 && !IsInstance(unwrappedSymbol))
{
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_ObjectRequired, unwrappedSymbol) : null;
return LookupResult.StaticInstanceMismatch(symbol, diagInfo);
}
else if ((options & LookupOptions.MustNotBeInstance) != 0 && IsInstance(unwrappedSymbol))
{
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_ObjectProhibited, unwrappedSymbol) : null;
return LookupResult.StaticInstanceMismatch(symbol, diagInfo);
}
else if ((options & LookupOptions.MustNotBeNamespace) != 0 && unwrappedSymbol.Kind == SymbolKind.Namespace)
{
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_BadSKunknown, unwrappedSymbol, unwrappedSymbol.GetKindText()) : null;
return LookupResult.NotTypeOrNamespace(symbol, diagInfo);
}
else if ((options & LookupOptions.LabelsOnly) != 0 && unwrappedSymbol.Kind != SymbolKind.Label)
{
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_LabelNotFound, unwrappedSymbol.Name) : null;
return LookupResult.NotLabel(symbol, diagInfo);
}
else
{
return LookupResult.Good(symbol);
}
bool IsBadIvtSpecification()
{
// Ensures that during binding we don't ask for public key which results in attribute binding and stack overflow.
// If looking up attributes, don't ask for public key.
if ((unwrappedSymbol.DeclaredAccessibility == Accessibility.Internal ||
unwrappedSymbol.DeclaredAccessibility == Accessibility.ProtectedAndInternal ||
unwrappedSymbol.DeclaredAccessibility == Accessibility.ProtectedOrInternal)
&& !options.IsAttributeTypeLookup())
{
var assemblyName = this.Compilation.AssemblyName;
if (assemblyName == null)
{
return false;
}
var keys = unwrappedSymbol.ContainingAssembly.GetInternalsVisibleToPublicKeys(assemblyName);
if (!keys.Any())
{
return false;
}
ImmutableArray<byte> publicKey = this.Compilation.Assembly.PublicKey;
if (!publicKey.IsDefault)
{
foreach (ImmutableArray<byte> key in keys)
{
if (key.SequenceEqual(publicKey))
{
return false;
}
}
}
return true;
}
return false;
}
}
private CSDiagnosticInfo MakeCallMethodsDirectlyDiagnostic(Symbol symbol)
{
Debug.Assert(symbol.MustCallMethodsDirectly());
MethodSymbol method1;
MethodSymbol method2;
switch (symbol.Kind)
{
case SymbolKind.Property:
{
var property = ((PropertySymbol)symbol).GetLeastOverriddenProperty(this.ContainingType);
method1 = property.GetMethod;
method2 = property.SetMethod;
}
break;
case SymbolKind.Event:
{
var @event = ((EventSymbol)symbol).GetLeastOverriddenEvent(this.ContainingType);
method1 = @event.AddMethod;
method2 = @event.RemoveMethod;
}
break;
default:
throw ExceptionUtilities.UnexpectedValue(symbol.Kind);
}
return (((object)method1 != null) && ((object)method2 != null)) ?
new CSDiagnosticInfo(ErrorCode.ERR_BindToBogusProp2, symbol, method1, method2) :
new CSDiagnosticInfo(ErrorCode.ERR_BindToBogusProp1, symbol, method1 ?? method2);
}
/// <summary>
/// Used by Add*LookupSymbolsInfo* to determine whether the symbol is of interest.
/// Distinguish from <see cref="CheckViability"/>, which performs an analogous task for LookupSymbols*.
/// </summary>
/// <remarks>
/// Does not consider <see cref="Symbol.CanBeReferencedByName"/> - that is left to the caller.
/// </remarks>
internal bool CanAddLookupSymbolInfo(Symbol symbol, LookupOptions options, LookupSymbolsInfo info, TypeSymbol accessThroughType, AliasSymbol aliasSymbol = null)
{
Debug.Assert(symbol.Kind != SymbolKind.Alias, "It is the caller's responsibility to unwrap aliased symbols.");
Debug.Assert(aliasSymbol == null || aliasSymbol.GetAliasTarget(basesBeingResolved: null) == symbol);
Debug.Assert(options.AreValid());
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
var name = aliasSymbol != null ? aliasSymbol.Name : symbol.Name;
if (!info.CanBeAdded(name))
{
return false;
}
if ((options & LookupOptions.NamespacesOrTypesOnly) != 0 && !(symbol is NamespaceOrTypeSymbol))
{
return false;
}
else if ((options & LookupOptions.MustBeInvocableIfMember) != 0
&& IsNonInvocableMember(symbol))
{
return false;
}
else if (InCref ? !this.IsCrefAccessible(symbol)
: !this.IsAccessible(symbol, ref discardedUseSiteInfo, RefineAccessThroughType(options, accessThroughType)))
{
return false;
}
else if (!IsInScopeOfAssociatedSyntaxTree(symbol))
{
return false;
}
else if ((options & LookupOptions.MustBeInstance) != 0 && !IsInstance(symbol))
{
return false;
}
else if ((options & LookupOptions.MustNotBeInstance) != 0 && IsInstance(symbol))
{
return false;
}
else if ((options & LookupOptions.MustNotBeNamespace) != 0 && (symbol.Kind == SymbolKind.Namespace))
{
return false;
}
else
{
// This viability check is only used by SemanticModel.LookupSymbols, which does its own
// filtering of not-referenceable symbols. Hence, we do not check CanBeReferencedByName
// here.
return true;
}
}
private static TypeSymbol RefineAccessThroughType(LookupOptions options, TypeSymbol accessThroughType)
{
// Normally, when we access a protected instance member, we need to know the type of the receiver so we
// can determine whether the member is actually accessible in the containing type. There is one exception:
// If the receiver is "base", then it's okay if the receiver type isn't derived from the containing type.
return ((options & LookupOptions.UseBaseReferenceAccessibility) != 0)
? null
: accessThroughType;
}
/// <summary>
/// A symbol is accessible for referencing in a cref if it is in the same assembly as the reference
/// or the symbols's effective visibility is not private.
/// </summary>
private bool IsCrefAccessible(Symbol symbol)
{
return !IsEffectivelyPrivate(symbol) || symbol.ContainingAssembly == this.Compilation.Assembly;
}
private static bool IsEffectivelyPrivate(Symbol symbol)
{
for (Symbol s = symbol; (object)s != null; s = s.ContainingSymbol)
{
if (s.DeclaredAccessibility == Accessibility.Private)
{
return true;
}
}
return false;
}
/// <summary>
/// Check whether "symbol" is accessible from this binder.
/// Also checks protected access via "accessThroughType".
/// </summary>
internal bool IsAccessible(Symbol symbol, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, TypeSymbol accessThroughType = null, ConsList<TypeSymbol> basesBeingResolved = null)
{
bool failedThroughTypeCheck;
return IsAccessible(symbol, accessThroughType, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved);
}
internal bool IsAccessible(Symbol symbol, SyntaxNode syntax, BindingDiagnosticBag diagnostics)
{
var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var result = IsAccessible(symbol, ref useSiteInfo);
diagnostics.Add(syntax, useSiteInfo);
return result;
}
/// <summary>
/// Check whether "symbol" is accessible from this binder.
/// Also checks protected access via "accessThroughType", and sets "failedThroughTypeCheck" if fails
/// the protected access check.
/// </summary>
internal bool IsAccessible(Symbol symbol, TypeSymbol accessThroughType, out bool failedThroughTypeCheck, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved = null)
{
if (this.Flags.Includes(BinderFlags.IgnoreAccessibility))
{
failedThroughTypeCheck = false;
return true;
}
return IsAccessibleHelper(symbol, accessThroughType, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved);
}
/// <remarks>
/// Should only be called by <see cref="IsAccessible(Symbol, TypeSymbol, out bool, ref CompoundUseSiteInfo{AssemblySymbol}, ConsList{TypeSymbol})"/>,
/// which will already have checked for <see cref="BinderFlags.IgnoreAccessibility"/>.
/// </remarks>
internal virtual bool IsAccessibleHelper(Symbol symbol, TypeSymbol accessThroughType, out bool failedThroughTypeCheck, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo, ConsList<TypeSymbol> basesBeingResolved)
{
// By default, just delegate to containing binder.
return Next.IsAccessibleHelper(symbol, accessThroughType, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved);
}
internal bool IsNonInvocableMember(Symbol symbol)
{
switch (symbol.Kind)
{
case SymbolKind.Method:
case SymbolKind.Field:
case SymbolKind.Property:
case SymbolKind.NamedType:
case SymbolKind.Event:
return !IsInvocableMember(symbol);
default:
return false;
}
}
private bool IsInvocableMember(Symbol symbol)
{
// If a member is a method or event, or if it is a constant, field or property of
// either a delegate type or the type dynamic, then the member is said to be invocable.
TypeSymbol type = null;
switch (symbol.Kind)
{
case SymbolKind.Method:
case SymbolKind.Event: // Spec says it doesn't matter whether it is field-like
return true;
case SymbolKind.Field:
type = ((FieldSymbol)symbol).GetFieldType(this.FieldsBeingBound).Type;
break;
case SymbolKind.Property:
type = ((PropertySymbol)symbol).Type;
break;
}
return (object)type != null && (type.IsDelegateType() || type.IsDynamic() || type.IsFunctionPointer());
}
private static bool IsInstance(Symbol symbol)
{
switch (symbol.Kind)
{
case SymbolKind.Field:
case SymbolKind.Property:
case SymbolKind.Method:
case SymbolKind.Event:
return symbol.RequiresInstanceReceiver();
default:
return false;
}
}
// Check if the given symbol can be accessed with the given arity. If OK, return false.
// If not OK, return true and return a diagnosticinfo. Note that methods with type arguments
// can be accesses with arity zero due to type inference (but non types).
private static bool WrongArity(Symbol symbol, int arity, bool diagnose, LookupOptions options, out DiagnosticInfo diagInfo)
{
switch (symbol.Kind)
{
case SymbolKind.NamedType:
if (arity != 0 || (options & LookupOptions.AllNamedTypesOnArityZero) == 0)
{
NamedTypeSymbol namedType = (NamedTypeSymbol)symbol;
// non-declared types only appear as using aliases (aliases are arity 0)
Debug.Assert(object.ReferenceEquals(namedType.ConstructedFrom, namedType));
if (namedType.Arity != arity)
{
if (namedType.Arity == 0)
{
// The non-generic {1} '{0}' cannot be used with type arguments
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_HasNoTypeVars, namedType, MessageID.IDS_SK_TYPE.Localize()) : null;
}
else
{
// Using the generic {1} '{0}' requires {2} type arguments
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_BadArity, namedType, MessageID.IDS_SK_TYPE.Localize(), namedType.Arity) : null;
}
return true;
}
}
break;
case SymbolKind.Method:
if (arity != 0 || (options & LookupOptions.AllMethodsOnArityZero) == 0)
{
MethodSymbol method = (MethodSymbol)symbol;
if (method.Arity != arity)
{
if (method.Arity == 0)
{
// The non-generic {1} '{0}' cannot be used with type arguments
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_HasNoTypeVars, method, MessageID.IDS_SK_METHOD.Localize()) : null;
}
else
{
// Using the generic {1} '{0}' requires {2} type arguments
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_BadArity, method, MessageID.IDS_SK_METHOD.Localize(), method.Arity) : null;
}
return true;
}
}
break;
default:
if (arity != 0)
{
diagInfo = diagnose ? new CSDiagnosticInfo(ErrorCode.ERR_TypeArgsNotAllowed, symbol, symbol.Kind.Localize()) : null;
return true;
}
break;
}
diagInfo = null;
return false;
}
/// <summary>
/// Look for names in scope
/// </summary>
internal void AddLookupSymbolsInfo(LookupSymbolsInfo result, LookupOptions options = LookupOptions.Default)
{
for (var scope = this; scope != null; scope = scope.Next)
{
scope.AddLookupSymbolsInfoInSingleBinder(result, options, originalBinder: this);
if ((options & LookupOptions.LabelsOnly) != 0 && scope.IsLastBinderWithinMember())
{
// Labels declared outside of a member are not visible inside.
break;
}
}
}
internal virtual void AddLookupSymbolsInfoInSingleBinder(LookupSymbolsInfo info, LookupOptions options, Binder originalBinder)
{
// overridden in other binders
}
/// <summary>
/// Look for names of members
/// </summary>
internal void AddMemberLookupSymbolsInfo(LookupSymbolsInfo result, NamespaceOrTypeSymbol nsOrType, LookupOptions options, Binder originalBinder)
{
if (nsOrType.IsNamespace)
{
AddMemberLookupSymbolsInfoInNamespace(result, (NamespaceSymbol)nsOrType, options, originalBinder);
}
else
{
this.AddMemberLookupSymbolsInfoInType(result, (TypeSymbol)nsOrType, options, originalBinder);
}
}
private void AddMemberLookupSymbolsInfoInType(LookupSymbolsInfo result, TypeSymbol type, LookupOptions options, Binder originalBinder)
{
switch (type.TypeKind)
{
case TypeKind.TypeParameter:
this.AddMemberLookupSymbolsInfoInTypeParameter(result, (TypeParameterSymbol)type, options, originalBinder);
break;
case TypeKind.Interface:
this.AddMemberLookupSymbolsInfoInInterface(result, type, options, originalBinder, type);
break;
case TypeKind.Class:
case TypeKind.Struct:
case TypeKind.Enum:
case TypeKind.Delegate:
case TypeKind.Array:
case TypeKind.Dynamic:
case TypeKind.Submission:
this.AddMemberLookupSymbolsInfoInClass(result, type, options, originalBinder, type);
break;
}
}
protected void AddMemberLookupSymbolsInfoInSubmissions(LookupSymbolsInfo result, TypeSymbol scriptClass, bool inUsings, LookupOptions options, Binder originalBinder)
{
// TODO: we need tests
// TODO: optimize lookup (there might be many interactions in the chain)
for (CSharpCompilation submission = Compilation; submission != null; submission = submission.PreviousSubmission)
{
if ((object)submission.ScriptClass != null)
{
AddMemberLookupSymbolsInfoWithoutInheritance(result, submission.ScriptClass, options, originalBinder, scriptClass);
}
bool isCurrentSubmission = submission == Compilation;
// If we are looking only for labels we do not need to search through the imports.
if ((options & LookupOptions.LabelsOnly) == 0 && !(isCurrentSubmission && inUsings))
{
var submissionImports = submission.GetSubmissionImports();
if (!isCurrentSubmission)
{
submissionImports = Imports.ExpandPreviousSubmissionImports(submissionImports, Compilation);
}
// NB: Here we only look in aliases.
// In submissions, regular usings are bubbled up to the outermost scope.
AddLookupSymbolsInfoInAliases(submissionImports.UsingAliases, submissionImports.ExternAliases, result, options, originalBinder);
}
}
}
protected void AddLookupSymbolsInfoInAliases(
ImmutableDictionary<string, AliasAndUsingDirective> usingAliases,
ImmutableArray<AliasAndExternAliasDirective> externAliases,
LookupSymbolsInfo result, LookupOptions options, Binder originalBinder)
{
// If we are looking only for labels we do not need to search through the imports.
if ((options & LookupOptions.LabelsOnly) == 0)
{
foreach (var pair in usingAliases)
{
addAliasSymbolToResult(result, pair.Value.Alias, options, originalBinder);
}
foreach (var externAlias in externAliases)
{
if (!externAlias.SkipInLookup)
{
addAliasSymbolToResult(result, externAlias.Alias, options, originalBinder);
}
}
}
static void addAliasSymbolToResult(LookupSymbolsInfo result, AliasSymbol aliasSymbol, LookupOptions options, Binder originalBinder)
{
var targetSymbol = aliasSymbol.GetAliasTarget(basesBeingResolved: null);
if (originalBinder.CanAddLookupSymbolInfo(targetSymbol, options, result, accessThroughType: null, aliasSymbol: aliasSymbol))
{
result.AddSymbol(aliasSymbol, aliasSymbol.Name, 0);
}
}
}
private static void AddMemberLookupSymbolsInfoInNamespace(LookupSymbolsInfo result, NamespaceSymbol ns, LookupOptions options, Binder originalBinder)
{
var candidateMembers = result.FilterName != null ? GetCandidateMembers(ns, result.FilterName, options, originalBinder) : GetCandidateMembers(ns, options, originalBinder);
foreach (var symbol in candidateMembers)
{
if (originalBinder.CanAddLookupSymbolInfo(symbol, options, result, null))
{
result.AddSymbol(symbol, symbol.Name, symbol.GetArity());
}
}
}
private static void AddMemberLookupSymbolsInfoWithoutInheritance(LookupSymbolsInfo result, TypeSymbol type, LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType)
{
var candidateMembers = result.FilterName != null ? GetCandidateMembers(type, result.FilterName, options, originalBinder) : GetCandidateMembers(type, options, originalBinder);
foreach (var symbol in candidateMembers)
{
if (originalBinder.CanAddLookupSymbolInfo(symbol, options, result, accessThroughType))
{
result.AddSymbol(symbol, symbol.Name, symbol.GetArity());
}
}
}
private void AddWinRTMembersLookupSymbolsInfo(LookupSymbolsInfo result, NamedTypeSymbol type, LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType)
{
NamedTypeSymbol idictSymbol, iroDictSymbol, iListSymbol, iCollectionSymbol, inccSymbol, inpcSymbol;
GetWellKnownWinRTMemberInterfaces(out idictSymbol, out iroDictSymbol, out iListSymbol, out iCollectionSymbol, out inccSymbol, out inpcSymbol);
// Dev11 searches all declared and undeclared base interfaces
foreach (var iface in type.AllInterfacesNoUseSiteDiagnostics)
{
if (ShouldAddWinRTMembersForInterface(iface, idictSymbol, iroDictSymbol, iListSymbol, iCollectionSymbol, inccSymbol, inpcSymbol))
{
AddMemberLookupSymbolsInfoWithoutInheritance(result, iface, options, originalBinder, accessThroughType);
}
}
}
private void AddMemberLookupSymbolsInfoInClass(LookupSymbolsInfo result, TypeSymbol type, LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType)
{
PooledHashSet<NamedTypeSymbol> visited = null;
// We need a check for SpecialType.System_Void as its base type is
// ValueType but we don't wish to return any members for void type
while ((object)type != null && !type.IsVoidType())
{
AddMemberLookupSymbolsInfoWithoutInheritance(result, type, options, originalBinder, accessThroughType);
// If the type is from a winmd and implements any of the special WinRT collection
// projections then we may need to add underlying interface members.
NamedTypeSymbol namedType = type as NamedTypeSymbol;
if ((object)namedType != null && namedType.ShouldAddWinRTMembers)
{
AddWinRTMembersLookupSymbolsInfo(result, namedType, options, originalBinder, accessThroughType);
}
// As in dev11, we don't consider inherited members within crefs.
// CAVEAT: dev11 appears to ignore this rule within parameter types and return types,
// so we're checking Cref, rather than Cref and CrefParameterOrReturnType.
if (originalBinder.InCrefButNotParameterOrReturnType)
{
break;
}
type = type.GetNextBaseTypeNoUseSiteDiagnostics(null, this.Compilation, ref visited);
}
visited?.Free();
}
private void AddMemberLookupSymbolsInfoInInterface(LookupSymbolsInfo result, TypeSymbol type, LookupOptions options, Binder originalBinder, TypeSymbol accessThroughType)
{
AddMemberLookupSymbolsInfoWithoutInheritance(result, type, options, originalBinder, accessThroughType);
if (!originalBinder.InCrefButNotParameterOrReturnType)
{
foreach (var baseInterface in type.AllInterfacesNoUseSiteDiagnostics)
{
AddMemberLookupSymbolsInfoWithoutInheritance(result, baseInterface, options, originalBinder, accessThroughType);
}
this.AddMemberLookupSymbolsInfoInClass(result, Compilation.GetSpecialType(SpecialType.System_Object), options, originalBinder, accessThroughType);
}
}
private void AddMemberLookupSymbolsInfoInTypeParameter(LookupSymbolsInfo result, TypeParameterSymbol type, LookupOptions options, Binder originalBinder)
{
if (type.TypeParameterKind == TypeParameterKind.Cref)
{
return;
}
NamedTypeSymbol effectiveBaseClass = type.EffectiveBaseClassNoUseSiteDiagnostics;
this.AddMemberLookupSymbolsInfoInClass(result, effectiveBaseClass, options, originalBinder, effectiveBaseClass);
foreach (var baseInterface in type.AllEffectiveInterfacesNoUseSiteDiagnostics)
{
// accessThroughType matches LookupMembersInTypeParameter.
AddMemberLookupSymbolsInfoWithoutInheritance(result, baseInterface, options, originalBinder, accessThroughType: type);
}
}
}
}
|