|
// 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.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
/// <summary>
/// A Binder converts names in to symbols and syntax nodes into bound trees. It is context
/// dependent, relative to a location in source code.
/// </summary>
internal partial class Binder
{
internal CSharpCompilation Compilation { get; }
internal readonly BinderFlags Flags;
/// <summary>
/// Used to create a root binder.
/// </summary>
internal Binder(CSharpCompilation compilation)
{
RoslynDebug.Assert(compilation != null);
RoslynDebug.Assert(this is BuckStopsHereBinder);
this.Flags = compilation.Options.TopLevelBinderFlags;
this.Compilation = compilation;
}
internal Binder(Binder next, Conversions? conversions = null)
{
RoslynDebug.Assert(next != null);
Next = next;
this.Flags = next.Flags;
this.Compilation = next.Compilation;
_lazyConversions = conversions;
}
protected Binder(Binder next, BinderFlags flags)
{
RoslynDebug.Assert(next != null);
// Mutually exclusive.
RoslynDebug.Assert(!flags.Includes(BinderFlags.UncheckedRegion | BinderFlags.CheckedRegion));
// Implied.
RoslynDebug.Assert(!flags.Includes(BinderFlags.InNestedFinallyBlock) || flags.Includes(BinderFlags.InFinallyBlock | BinderFlags.InCatchBlock));
Next = next;
this.Flags = flags;
this.Compilation = next.Compilation;
}
internal bool IsSemanticModelBinder
{
get
{
return this.Flags.Includes(BinderFlags.SemanticModel);
}
}
// IsEarlyAttributeBinder is called relatively frequently so we want fast code here.
internal bool IsEarlyAttributeBinder
{
get
{
return this.Flags.Includes(BinderFlags.EarlyAttributeBinding);
}
}
// Return the nearest enclosing node being bound as a nameof(...) argument, if any, or null if none.
protected virtual SyntaxNode? EnclosingNameofArgument => NextRequired.EnclosingNameofArgument;
internal virtual bool IsInsideNameof => NextRequired.IsInsideNameof;
/// <summary>
/// Get the next binder in which to look up a name, if not found by this binder.
/// </summary>
protected internal Binder? Next { get; }
/// <summary>
/// Get the next binder in which to look up a name, if not found by this binder, asserting if `Next` is null.
/// </summary>
protected internal Binder NextRequired
{
get
{
Debug.Assert(Next is not null);
return Next;
}
}
/// <summary>
/// <see cref="OverflowChecks.Enabled"/> if we are in an explicitly checked context (within checked block or expression).
/// <see cref="OverflowChecks.Disabled"/> if we are in an explicitly unchecked context (within unchecked block or expression).
/// <see cref="OverflowChecks.Implicit"/> otherwise.
/// </summary>
protected OverflowChecks CheckOverflow
{
get
{
// Although C# 4.0 specification says that checked context never flows in a lambda,
// the Dev10 compiler implementation always flows the context in, except for
// when the lambda is directly a "parameter" of the checked/unchecked expression.
// For Roslyn we decided to change the spec and always flow the context in.
// So we don't stop at lambda binder.
RoslynDebug.Assert(!this.Flags.Includes(BinderFlags.UncheckedRegion | BinderFlags.CheckedRegion));
return this.Flags.Includes(BinderFlags.CheckedRegion)
? OverflowChecks.Enabled
: this.Flags.Includes(BinderFlags.UncheckedRegion)
? OverflowChecks.Disabled
: OverflowChecks.Implicit;
}
}
/// <summary>
/// True if instructions that check overflow should be generated.
/// </summary>
/// <remarks>
/// Spec 7.5.12:
/// For non-constant expressions (expressions that are evaluated at run-time) that are not
/// enclosed by any checked or unchecked operators or statements, the default overflow checking
/// context is unchecked unless external factors (such as compiler switches and execution
/// environment configuration) call for checked evaluation.
/// </remarks>
internal bool CheckOverflowAtRuntime
{
get
{
var result = CheckOverflow;
return result == OverflowChecks.Enabled || result == OverflowChecks.Implicit && Compilation.Options.CheckOverflow;
}
}
/// <summary>
/// True if the compiler should check for overflow while evaluating constant expressions.
/// </summary>
/// <remarks>
/// Spec 7.5.12:
/// For constant expressions (expressions that can be fully evaluated at compile-time),
/// the default overflow checking context is always checked. Unless a constant expression
/// is explicitly placed in an unchecked context, overflows that occur during the compile-time
/// evaluation of the expression always cause compile-time errors.
/// </remarks>
internal bool CheckOverflowAtCompileTime
{
get
{
return CheckOverflow != OverflowChecks.Disabled;
}
}
internal bool UseUpdatedEscapeRules => Compilation.SourceModule.UseUpdatedEscapeRules;
/// <summary>
/// Some nodes have special binders for their contents (like Blocks)
/// </summary>
internal virtual Binder? GetBinder(SyntaxNode node)
{
RoslynDebug.Assert(Next is object);
return this.Next.GetBinder(node);
}
/// <summary>
/// Gets a binder for a node that must be not null, and asserts
/// if it is not.
/// </summary>
internal Binder GetRequiredBinder(SyntaxNode node)
{
var binder = GetBinder(node);
RoslynDebug.Assert(binder is object);
return binder;
}
/// <summary>
/// Get locals declared immediately in scope designated by the node.
/// </summary>
internal virtual ImmutableArray<LocalSymbol> GetDeclaredLocalsForScope(SyntaxNode scopeDesignator)
{
RoslynDebug.Assert(Next is object);
return this.Next.GetDeclaredLocalsForScope(scopeDesignator);
}
/// <summary>
/// Get local functions declared immediately in scope designated by the node.
/// </summary>
internal virtual ImmutableArray<LocalFunctionSymbol> GetDeclaredLocalFunctionsForScope(CSharpSyntaxNode scopeDesignator)
{
RoslynDebug.Assert(Next is object);
return this.Next.GetDeclaredLocalFunctionsForScope(scopeDesignator);
}
/// <summary>
/// If this binder owns a scope for locals, return syntax node that is used
/// as the scope designator. Otherwise, null.
/// </summary>
internal virtual SyntaxNode? ScopeDesignator
{
get
{
return null;
}
}
internal virtual bool IsLocalFunctionsScopeBinder
{
get
{
return false;
}
}
internal virtual bool IsLabelsScopeBinder
{
get
{
return false;
}
}
internal bool InExpressionTree => (Flags & BinderFlags.InExpressionTree) == BinderFlags.InExpressionTree;
/// <summary>
/// True if this is the top-level binder for a local function or lambda
/// (including implicit lambdas from query expressions).
/// </summary>
internal virtual bool IsNestedFunctionBinder => false;
/// <summary>
/// The member containing the binding context. Note that for the purposes of the compiler,
/// a lambda expression is considered a "member" of its enclosing method, field, or lambda.
/// </summary>
internal virtual Symbol? ContainingMemberOrLambda
{
get
{
RoslynDebug.Assert(Next is object);
return Next.ContainingMemberOrLambda;
}
}
/// <summary>
/// Are we in a context where un-annotated types should be interpreted as non-null?
/// </summary>
internal bool AreNullableAnnotationsEnabled(SyntaxTree syntaxTree, int position)
{
CSharpSyntaxTree csTree = (CSharpSyntaxTree)syntaxTree;
Syntax.NullableContextState context = csTree.GetNullableContextState(position);
return context.AnnotationsState switch
{
Syntax.NullableContextState.State.Enabled => true,
Syntax.NullableContextState.State.Disabled => false,
Syntax.NullableContextState.State.ExplicitlyRestored => GetGlobalAnnotationState(),
Syntax.NullableContextState.State.Unknown =>
// IsGeneratedCode may be slow, check global state first:
AreNullableAnnotationsGloballyEnabled() &&
!csTree.IsGeneratedCode(this.Compilation.Options.SyntaxTreeOptionsProvider, CancellationToken.None),
_ => throw ExceptionUtilities.UnexpectedValue(context.AnnotationsState)
};
}
internal bool AreNullableAnnotationsEnabled(SyntaxToken token)
{
RoslynDebug.Assert(token.SyntaxTree is object);
return AreNullableAnnotationsEnabled(token.SyntaxTree, token.SpanStart);
}
internal virtual bool AreNullableAnnotationsGloballyEnabled()
{
RoslynDebug.Assert(Next is object);
return Next.AreNullableAnnotationsGloballyEnabled();
}
protected bool GetGlobalAnnotationState()
{
switch (Compilation.Options.NullableContextOptions)
{
case NullableContextOptions.Enable:
case NullableContextOptions.Annotations:
return true;
case NullableContextOptions.Disable:
case NullableContextOptions.Warnings:
return false;
default:
throw ExceptionUtilities.UnexpectedValue(Compilation.Options.NullableContextOptions);
}
}
/// <summary>
/// Is the contained code within a member method body?
/// </summary>
/// <remarks>
/// May be false in lambdas that are outside of member method bodies, e.g. lambdas in
/// field initializers.
/// </remarks>
internal virtual bool IsInMethodBody
{
get
{
RoslynDebug.Assert(Next is object);
return Next.IsInMethodBody;
}
}
/// <summary>
/// Is the contained code within an iterator block?
/// </summary>
/// <remarks>
/// Will be false in a lambda in an iterator.
/// </remarks>
internal virtual bool IsDirectlyInIterator
{
get
{
RoslynDebug.Assert(Next is object);
return Next.IsDirectlyInIterator;
}
}
/// <summary>
/// Is the contained code within the syntactic span of an
/// iterator method?
/// </summary>
/// <remarks>
/// Will be true in a lambda in an iterator.
/// </remarks>
internal virtual bool IsIndirectlyInIterator
{
get
{
RoslynDebug.Assert(Next is object);
return Next.IsIndirectlyInIterator;
}
}
/// <summary>
/// If we are inside a context where a break statement is legal,
/// returns the <see cref="GeneratedLabelSymbol"/> that a break statement would branch to.
/// Returns null otherwise.
/// </summary>
internal virtual GeneratedLabelSymbol? BreakLabel
{
get
{
RoslynDebug.Assert(Next is object);
return Next.BreakLabel;
}
}
/// <summary>
/// If we are inside a context where a continue statement is legal,
/// returns the <see cref="GeneratedLabelSymbol"/> that a continue statement would branch to.
/// Returns null otherwise.
/// </summary>
internal virtual GeneratedLabelSymbol? ContinueLabel
{
get
{
RoslynDebug.Assert(Next is object);
return Next.ContinueLabel;
}
}
/// <summary>
/// Get the element type of this iterator.
/// </summary>
/// <returns>Element type of the current iterator, or an error type.</returns>
internal virtual TypeWithAnnotations GetIteratorElementType()
{
RoslynDebug.Assert(Next is object);
return Next.GetIteratorElementType();
}
/// <summary>
/// The imports for all containing namespace declarations (innermost-to-outermost, including global),
/// or null if there are none.
/// </summary>
internal virtual ImportChain? ImportChain
{
get
{
RoslynDebug.Assert(Next is object);
return Next.ImportChain;
}
}
/// <summary>
/// Get <see cref="QuickAttributeChecker"/> that can be used to quickly
/// check for certain attribute applications in context of this binder.
/// </summary>
internal virtual QuickAttributeChecker QuickAttributeChecker
{
get
{
RoslynDebug.Assert(Next is object);
return Next.QuickAttributeChecker;
}
}
protected virtual bool InExecutableBinder
{
get
{
RoslynDebug.Assert(Next is object);
return Next.InExecutableBinder;
}
}
/// <summary>
/// The type containing the binding context
/// </summary>
internal NamedTypeSymbol? ContainingType
{
get
{
var member = this.ContainingMemberOrLambda;
RoslynDebug.Assert(member is null || member.Kind != SymbolKind.ErrorType);
return member switch
{
null => null,
NamedTypeSymbol namedType => namedType,
_ => member.ContainingType
};
}
}
/// <summary>
/// Returns true if the binder is binding top-level script code.
/// </summary>
internal bool BindingTopLevelScriptCode
{
get
{
var containingMember = this.ContainingMemberOrLambda;
switch (containingMember?.Kind)
{
case SymbolKind.Method:
// global statements
return ((MethodSymbol)containingMember).IsScriptInitializer;
case SymbolKind.NamedType:
// script variable initializers
return ((NamedTypeSymbol)containingMember).IsScriptClass;
default:
return false;
}
}
}
internal virtual ConstantFieldsInProgress ConstantFieldsInProgress
{
get
{
RoslynDebug.Assert(Next is object);
return this.Next.ConstantFieldsInProgress;
}
}
internal virtual ConsList<FieldSymbol> FieldsBeingBound
{
get
{
RoslynDebug.Assert(Next is object);
return this.Next.FieldsBeingBound;
}
}
internal virtual LocalSymbol? LocalInProgress
{
get
{
RoslynDebug.Assert(Next is object);
return this.Next.LocalInProgress;
}
}
internal virtual NamedTypeSymbol? ParamsCollectionTypeInProgress => null;
internal virtual MethodSymbol? ParamsCollectionConstructorInProgress => null;
internal virtual BoundExpression? ConditionalReceiverExpression
{
get
{
RoslynDebug.Assert(Next is object);
return this.Next.ConditionalReceiverExpression;
}
}
private Conversions? _lazyConversions;
internal Conversions Conversions
{
get
{
if (_lazyConversions == null)
{
Interlocked.CompareExchange(ref _lazyConversions, new Conversions(this), null);
}
return _lazyConversions;
}
}
private OverloadResolution? _lazyOverloadResolution;
internal OverloadResolution OverloadResolution
{
get
{
if (_lazyOverloadResolution == null)
{
Interlocked.CompareExchange(ref _lazyOverloadResolution, new OverloadResolution(this), null);
}
return _lazyOverloadResolution;
}
}
internal static void Error(BindingDiagnosticBag diagnostics, DiagnosticInfo info, SyntaxNode syntax)
{
diagnostics.Add(new CSDiagnostic(info, syntax.Location));
}
internal static void Error(BindingDiagnosticBag diagnostics, DiagnosticInfo info, Location location)
{
diagnostics.Add(new CSDiagnostic(info, location));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), syntax.Location));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, CSharpSyntaxNode syntax, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), syntax.Location));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxToken token)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), token.GetLocation()));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxToken token, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), token.GetLocation()));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax)
{
var location = syntax.GetLocation();
RoslynDebug.Assert(location is object);
Error(diagnostics, code, location);
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, SyntaxNodeOrToken syntax, params object[] args)
{
var location = syntax.GetLocation();
RoslynDebug.Assert(location is object);
Error(diagnostics, code, location, args);
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, Location location)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code), location));
}
internal static void Error(BindingDiagnosticBag diagnostics, ErrorCode code, Location location, params object[] args)
{
diagnostics.Add(new CSDiagnostic(new CSDiagnosticInfo(code, args), location));
}
/// <summary>
/// Issue an error or warning for a symbol if it is Obsolete. If there is not enough
/// information to report diagnostics, then store the symbols so that diagnostics
/// can be reported at a later stage.
/// </summary>
/// <remarks>
/// This method is introduced to move the implicit conversion operator call from the caller
/// so as to reduce the caller stack frame size
/// </remarks>
internal void ReportDiagnosticsIfObsolete(DiagnosticBag diagnostics, Symbol symbol, SyntaxNode node, bool hasBaseReceiver)
{
ReportDiagnosticsIfObsolete(diagnostics, symbol, (SyntaxNodeOrToken)node, hasBaseReceiver);
}
/// <summary>
/// Issue an error or warning for a symbol if it is Obsolete. If there is not enough
/// information to report diagnostics, then store the symbols so that diagnostics
/// can be reported at a later stage.
/// </summary>
internal void ReportDiagnosticsIfObsolete(DiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, bool hasBaseReceiver)
{
switch (symbol.Kind)
{
case SymbolKind.NamedType:
case SymbolKind.Field:
case SymbolKind.Method:
case SymbolKind.Event:
case SymbolKind.Property:
ReportDiagnosticsIfObsolete(diagnostics, symbol, node, hasBaseReceiver, this.ContainingMemberOrLambda, this.ContainingType, this.Flags);
break;
}
}
internal void ReportDiagnosticsIfObsolete(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, bool hasBaseReceiver)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsolete(diagnostics.DiagnosticBag, symbol, node, hasBaseReceiver);
}
}
internal void ReportDiagnosticsIfObsolete(BindingDiagnosticBag diagnostics, Conversion conversion, SyntaxNodeOrToken node, bool hasBaseReceiver)
{
if (conversion.IsValid && conversion.Method is object)
{
ReportDiagnosticsIfObsolete(diagnostics, conversion.Method, node, hasBaseReceiver);
}
}
internal static void ReportDiagnosticsIfObsolete(
DiagnosticBag diagnostics,
Symbol symbol,
SyntaxNodeOrToken node,
bool hasBaseReceiver,
Symbol? containingMember,
NamedTypeSymbol? containingType,
BinderFlags location)
{
RoslynDebug.Assert(symbol is object);
RoslynDebug.Assert(symbol.Kind == SymbolKind.NamedType ||
symbol.Kind == SymbolKind.Field ||
symbol.Kind == SymbolKind.Method ||
symbol.Kind == SymbolKind.Event ||
symbol.Kind == SymbolKind.Property);
// Dev11 also reports on the unconstructed method. It would be nice to report on
// the constructed method, but then we wouldn't be able to walk the override chain.
if (symbol.Kind == SymbolKind.Method)
{
symbol = ((MethodSymbol)symbol).ConstructedFrom;
}
// There are two reasons to walk up to the least-overridden member:
// 1) That's the method to which we will actually emit a call.
// 2) We don't know what virtual dispatch will do at runtime so an
// overriding member is basically a shot in the dark. Better to
// just be consistent and always use the least-overridden member.
Symbol leastOverriddenSymbol = symbol.GetLeastOverriddenMember(containingType);
bool checkOverridingSymbol = hasBaseReceiver && !ReferenceEquals(symbol, leastOverriddenSymbol);
if (checkOverridingSymbol)
{
// If we have a base receiver, we must be done with declaration binding, so it should
// be safe to decode diagnostics. We want to do this since reporting for the overriding
// member is conditional on reporting for the overridden member (i.e. we need a definite
// answer so we don't double-report). You might think that double reporting just results
// in cascading diagnostics, but it's possible that the second diagnostic is an error
// while the first is merely a warning.
leastOverriddenSymbol.GetAttributes();
}
var diagnosticKind = ReportDiagnosticsIfObsoleteInternal(diagnostics, leastOverriddenSymbol, node, containingMember, location);
// CONSIDER: In place of hasBaseReceiver, dev11 also accepts cases where symbol.ContainingType is a "simple type" (e.g. int)
// or a special by-ref type (e.g. ArgumentHandle). These cases are probably more important for other checks performed by
// ExpressionBinder::PostBindMethod, but they do appear to ObsoleteAttribute as well. We're skipping them because they
// don't make much sense for ObsoleteAttribute (e.g. this would seem to address the case where int.ToString has been made
// obsolete but object.ToString has not).
// If the overridden member was not definitely obsolete and this is a (non-virtual) base member
// access, then check the overriding symbol as well.
switch (diagnosticKind)
{
case ObsoleteDiagnosticKind.NotObsolete:
case ObsoleteDiagnosticKind.Lazy:
if (checkOverridingSymbol)
{
RoslynDebug.Assert(diagnosticKind != ObsoleteDiagnosticKind.Lazy, "We forced attribute binding above.");
ReportDiagnosticsIfObsoleteInternal(diagnostics, symbol, node, containingMember, location);
}
break;
}
}
internal static void ReportDiagnosticsIfObsolete(
BindingDiagnosticBag diagnostics,
Symbol symbol,
SyntaxNodeOrToken node,
bool hasBaseReceiver,
Symbol? containingMember,
NamedTypeSymbol? containingType,
BinderFlags location)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsolete(diagnostics.DiagnosticBag, symbol, node, hasBaseReceiver, containingMember, containingType, location);
}
}
internal static ObsoleteDiagnosticKind ReportDiagnosticsIfObsoleteInternal(DiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, Symbol? containingMember, BinderFlags location)
{
RoslynDebug.Assert(diagnostics != null);
var kind = ObsoleteAttributeHelpers.GetObsoleteDiagnosticKind(symbol, containingMember);
DiagnosticInfo? info = null;
switch (kind)
{
case ObsoleteDiagnosticKind.Diagnostic:
info = ObsoleteAttributeHelpers.CreateObsoleteDiagnostic(symbol, location);
break;
case ObsoleteDiagnosticKind.Lazy:
case ObsoleteDiagnosticKind.LazyPotentiallySuppressed:
info = new LazyObsoleteDiagnosticInfo(symbol, containingMember, location);
break;
}
if (info != null)
{
if (node.AsNode() is ForEachStatementSyntax foreachSyntax)
{
node = foreachSyntax.ForEachKeyword;
}
diagnostics.Add(info, node.GetLocation());
}
return kind;
}
internal static void ReportDiagnosticsIfObsoleteInternal(BindingDiagnosticBag diagnostics, Symbol symbol, SyntaxNodeOrToken node, Symbol containingMember, BinderFlags location)
{
if (diagnostics.DiagnosticBag is object)
{
ReportDiagnosticsIfObsoleteInternal(diagnostics.DiagnosticBag, symbol, node, containingMember, location);
}
}
internal static void ReportDiagnosticsIfUnmanagedCallersOnly(BindingDiagnosticBag diagnostics, MethodSymbol symbol, SyntaxNodeOrToken syntax, bool isDelegateConversion)
{
var unmanagedCallersOnlyAttributeData = symbol.GetUnmanagedCallersOnlyAttributeData(forceComplete: false);
if (unmanagedCallersOnlyAttributeData != null)
{
// Either we haven't yet bound the attributes of this method, or there is an UnmanagedCallersOnly present.
// In the former case, we use a lazy diagnostic that may end up being ignored later, to avoid causing a
// binding cycle.
Debug.Assert(syntax.GetLocation() != null);
diagnostics.Add(unmanagedCallersOnlyAttributeData == UnmanagedCallersOnlyAttributeData.Uninitialized
? new LazyUnmanagedCallersOnlyMethodCalledDiagnosticInfo(symbol, isDelegateConversion)
: new CSDiagnosticInfo(isDelegateConversion
? ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeConvertedToDelegate
: ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly,
symbol),
syntax.GetLocation()!);
}
}
internal static bool IsSymbolAccessibleConditional(
Symbol symbol,
AssemblySymbol within,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
return AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteInfo);
}
internal bool IsSymbolAccessibleConditional(
Symbol symbol,
NamedTypeSymbol within,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
TypeSymbol? throughTypeOpt = null)
{
return this.Flags.Includes(BinderFlags.IgnoreAccessibility) || AccessCheck.IsSymbolAccessible(symbol, within, ref useSiteInfo, throughTypeOpt);
}
internal bool IsSymbolAccessibleConditional(
Symbol symbol,
NamedTypeSymbol within,
TypeSymbol throughTypeOpt,
out bool failedThroughTypeCheck,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
ConsList<TypeSymbol>? basesBeingResolved = null)
{
if (this.Flags.Includes(BinderFlags.IgnoreAccessibility))
{
failedThroughTypeCheck = false;
return true;
}
return AccessCheck.IsSymbolAccessible(symbol, within, throughTypeOpt, out failedThroughTypeCheck, ref useSiteInfo, basesBeingResolved);
}
/// <summary>
/// Report diagnostics that should be reported when using a synthesized attribute.
/// </summary>
internal static void ReportUseSiteDiagnosticForSynthesizedAttribute(
CSharpCompilation compilation,
WellKnownMember attributeMember,
BindingDiagnosticBag diagnostics,
Location? location = null,
CSharpSyntaxNode? syntax = null)
{
RoslynDebug.Assert((location != null) ^ (syntax != null));
// Dev11 reports use-site diagnostics when an optional attribute is found but is bad for some other reason
// (comes from an unified assembly). When the symbol is not found no error is reported. See test VersionUnification_UseSiteDiagnostics_OptionalAttributes.
bool isOptional = WellKnownMembers.IsSynthesizedAttributeOptional(attributeMember);
GetWellKnownTypeMember(compilation, attributeMember, diagnostics, location, syntax, isOptional);
}
/// <summary>
/// Adds diagnostics that should be reported when using a synthesized attribute.
/// </summary>
internal static void AddUseSiteDiagnosticForSynthesizedAttribute(
CSharpCompilation compilation,
WellKnownMember attributeMember,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
GetWellKnownTypeMember(compilation,
attributeMember,
out var memberUseSiteInfo,
isOptional: WellKnownMembers.IsSynthesizedAttributeOptional(attributeMember));
useSiteInfo.Add(memberUseSiteInfo);
}
public CompoundUseSiteInfo<AssemblySymbol> GetNewCompoundUseSiteInfo(BindingDiagnosticBag futureDestination)
{
return new CompoundUseSiteInfo<AssemblySymbol>(futureDestination, Compilation.Assembly);
}
#if DEBUG
// Helper to allow displaying the binder hierarchy in the debugger.
internal Binder[] GetAllBinders()
{
var binders = ArrayBuilder<Binder>.GetInstance();
for (Binder? binder = this; binder != null; binder = binder.Next)
{
binders.Add(binder);
}
return binders.ToArrayAndFree();
}
#endif
internal BoundExpression WrapWithVariablesIfAny(CSharpSyntaxNode scopeDesignator, BoundExpression expression)
{
var locals = this.GetDeclaredLocalsForScope(scopeDesignator);
return (locals.IsEmpty)
? expression
: new BoundSequence(scopeDesignator, locals, ImmutableArray<BoundExpression>.Empty, expression, getType()) { WasCompilerGenerated = true };
TypeSymbol getType()
{
RoslynDebug.Assert(expression.Type is object);
return expression.Type;
}
}
internal BoundStatement WrapWithVariablesIfAny(CSharpSyntaxNode scopeDesignator, BoundStatement statement)
{
RoslynDebug.Assert(statement.Kind != BoundKind.StatementList);
var locals = this.GetDeclaredLocalsForScope(scopeDesignator);
if (locals.IsEmpty)
{
return statement;
}
return new BoundBlock(statement.Syntax, locals, ImmutableArray.Create(statement))
{ WasCompilerGenerated = true };
}
/// <summary>
/// Should only be used with scopes that could declare local functions.
/// </summary>
internal BoundStatement WrapWithVariablesAndLocalFunctionsIfAny(CSharpSyntaxNode scopeDesignator, BoundStatement statement)
{
var locals = this.GetDeclaredLocalsForScope(scopeDesignator);
var localFunctions = this.GetDeclaredLocalFunctionsForScope(scopeDesignator);
if (locals.IsEmpty && localFunctions.IsEmpty)
{
return statement;
}
return new BoundBlock(statement.Syntax, locals, localFunctions, hasUnsafeModifier: false, instrumentation: null,
ImmutableArray.Create(statement))
{ WasCompilerGenerated = true };
}
internal string Dump()
{
return TreeDumper.DumpCompact(dumpAncestors());
TreeDumperNode dumpAncestors()
{
TreeDumperNode? current = null;
for (Binder? scope = this; scope != null; scope = scope.Next)
{
var (description, snippet, locals) = print(scope);
var sub = new List<TreeDumperNode>();
if (!locals.IsEmpty())
{
sub.Add(new TreeDumperNode("locals", locals, null));
}
var currentContainer = scope.ContainingMemberOrLambda;
if (currentContainer != null && currentContainer != scope.Next?.ContainingMemberOrLambda)
{
sub.Add(new TreeDumperNode("containing symbol", currentContainer.ToDisplayString(), null));
}
if (snippet != null)
{
sub.Add(new TreeDumperNode($"scope", $"{snippet} ({scope.ScopeDesignator?.Kind()})", null));
}
if (current != null)
{
sub.Add(current);
}
current = new TreeDumperNode(description, null, sub);
}
RoslynDebug.Assert(current is object);
return current;
}
static (string description, string? snippet, string locals) print(Binder scope)
{
var locals = string.Join(", ", scope.Locals.SelectAsArray(s => s.Name));
string? snippet = null;
if (scope.ScopeDesignator != null)
{
var lines = scope.ScopeDesignator.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (lines.Length == 1)
{
snippet = lines[0];
}
else
{
var first = lines[0];
var last = lines[lines.Length - 1].Trim();
var lastSize = Math.Min(last.Length, 12);
snippet = first.Substring(0, Math.Min(first.Length, 12)) + " ... " + last.Substring(last.Length - lastSize, lastSize);
}
snippet = snippet.IsEmpty() ? null : snippet;
}
var description = scope.GetType().Name;
return (description, snippet, locals);
}
}
}
}
|