// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Semantics;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
///
/// Binding info for expressions and statements that are part of a member declaration.
///
internal abstract partial class MemberSemanticModel : CSharpSemanticModel
{
private readonly CSharpCompilation _compilation;
private readonly Symbol _memberSymbol;
private readonly CSharpSyntaxNode _root;
private readonly DiagnosticBag _ignoredDiagnostics = new DiagnosticBag();
private readonly ReaderWriterLockSlim _nodeMapLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
// The bound nodes associated with a syntax node, from highest in the tree to lowest.
private readonly Dictionary> _guardedNodeMap = new Dictionary>();
internal readonly Binder RootBinder;
// Fields specific to a speculative MemberSemanticModel.
private readonly SyntaxTreeSemanticModel _parentSemanticModelOpt;
private readonly int _speculatedPosition;
protected MemberSemanticModel(CSharpCompilation compilation, CSharpSyntaxNode root, Symbol memberSymbol, Binder rootBinder, SyntaxTreeSemanticModel parentSemanticModelOpt, int speculatedPosition)
{
Debug.Assert(compilation != null);
Debug.Assert(root != null);
Debug.Assert((object)memberSymbol != null);
Debug.Assert(parentSemanticModelOpt == null || !parentSemanticModelOpt.IsSpeculativeSemanticModel, CSharpResources.ChainingSpeculativeModelIsNotSupported);
_compilation = compilation;
_root = root;
_memberSymbol = memberSymbol;
this.RootBinder = rootBinder.WithAdditionalFlags(GetSemanticModelBinderFlags());
_parentSemanticModelOpt = parentSemanticModelOpt;
_speculatedPosition = speculatedPosition;
}
public override CSharpCompilation Compilation
{
get
{
return _compilation;
}
}
internal override CSharpSyntaxNode Root
{
get
{
return _root;
}
}
///
/// The member symbol
///
internal Symbol MemberSymbol
{
get
{
return _memberSymbol;
}
}
public override bool IsSpeculativeSemanticModel
{
get
{
return _parentSemanticModelOpt != null;
}
}
public override int OriginalPositionForSpeculation
{
get
{
return _speculatedPosition;
}
}
public override CSharpSemanticModel ParentModel
{
get
{
return _parentSemanticModelOpt;
}
}
internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
{
return IsInTree(node) ? this : null;
}
internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, TypeSyntax type, SpeculativeBindingOption bindingOption, out SemanticModel speculativeModel)
{
var expression = SyntaxFactory.GetStandaloneExpression(type);
var binder = this.GetSpeculativeBinder(position, expression, bindingOption);
if (binder != null)
{
speculativeModel = new SpeculativeMemberSemanticModel(parentModel, _memberSymbol, type, binder, position);
return true;
}
speculativeModel = null;
return false;
}
internal sealed override bool TryGetSpeculativeSemanticModelCore(SyntaxTreeSemanticModel parentModel, int position, CrefSyntax crefSyntax, out SemanticModel speculativeModel)
{
// crefs can never legally appear within members.
speculativeModel = null;
return false;
}
///
/// This overload exists for callers who
/// a) Already have a node in hand and don't want to search through the tree
/// b) May want to search from an indirect container (e.g. node containing node
/// containing position).
///
private Binder GetEnclosingBinder(SyntaxNode node, int position)
{
AssertPositionAdjusted(position);
return GetEnclosingBinder(node, position, RootBinder, _root).WithAdditionalFlags(GetSemanticModelBinderFlags());
}
private static Binder GetEnclosingBinder(SyntaxNode node, int position, Binder rootBinder, SyntaxNode root)
{
if (node == root)
{
return rootBinder.GetBinder(node) ?? rootBinder;
}
Debug.Assert(root.Contains(node));
ExpressionSyntax typeOfArgument = null;
SyntaxNode unexpectedAnonymousFunction = null;
// Keep track of which fix-up should be applied first. If we see a typeof expression inside an unexpected
// anonymous function, that the typeof binder should be innermost (i.e. should have the unexpected
// anonymous function binder as its Next).
// NOTE: only meaningful if typeOfArgument is non-null;
bool typeOfEncounteredBeforeUnexpectedAnonymousFunction = false;
Binder binder = null;
for (var current = node; binder == null; current = current.ParentOrStructuredTriviaParent)
{
Debug.Assert(current != null); // Why were we asked for an enclosing binder for a node outside our root?
StatementSyntax stmt = current as StatementSyntax;
TypeOfExpressionSyntax typeOfExpression;
SyntaxKind kind = current.Kind();
if (stmt != null)
{
if (LookupPosition.IsInStatementScope(position, stmt))
{
binder = rootBinder.GetBinder(current);
if (binder != null)
{
binder = AdjustBinderForPositionWithinStatement(position, binder, stmt);
}
}
}
else if (kind == SyntaxKind.CatchClause)
{
if (LookupPosition.IsInCatchBlockScope(position, (CatchClauseSyntax)current))
{
binder = rootBinder.GetBinder(current);
}
}
else if (kind == SyntaxKind.CatchFilterClause)
{
if (LookupPosition.IsInCatchFilterScope(position, (CatchFilterClauseSyntax)current))
{
binder = rootBinder.GetBinder(current);
}
}
else if (current.IsAnonymousFunction())
{
if (LookupPosition.IsInAnonymousFunctionOrQuery(position, current))
{
binder = rootBinder.GetBinder(current);
// This should only happen in error scenarios. For example, C# does not allow array rank
// specifiers in types, (e.g. int[1] x;), but the syntax model does. In order to construct
// an appropriate binder chain for the anonymous method body, we need to construct an
// ExecutableCodeBinder.
if (binder == null && unexpectedAnonymousFunction == null && current != root)
{
unexpectedAnonymousFunction = current;
}
}
}
else if (kind == SyntaxKind.TypeOfExpression &&
typeOfArgument == null &&
LookupPosition.IsBetweenTokens(
position,
(typeOfExpression = (TypeOfExpressionSyntax)current).OpenParenToken,
typeOfExpression.CloseParenToken))
{
typeOfArgument = typeOfExpression.Type;
typeOfEncounteredBeforeUnexpectedAnonymousFunction = unexpectedAnonymousFunction == null;
}
else if (kind == SyntaxKind.SwitchSection)
{
if (LookupPosition.IsInSwitchSectionScope(position, (SwitchSectionSyntax)current))
{
binder = rootBinder.GetBinder(current);
}
}
else if (kind == SyntaxKind.ArgumentList)
{
var argList = (ArgumentListSyntax)current;
if (LookupPosition.IsBetweenTokens(position, argList.OpenParenToken, argList.CloseParenToken))
{
binder = rootBinder.GetBinder(current);
}
}
else if (kind == SyntaxKind.EqualsValueClause)
{
binder = rootBinder.GetBinder(current);
}
else if (kind == SyntaxKind.Attribute)
{
binder = rootBinder.GetBinder(current);
}
else if (kind == SyntaxKind.ArrowExpressionClause)
{
binder = rootBinder.GetBinder(current);
}
else if ((current as ExpressionSyntax).IsValidScopeDesignator())
{
binder = rootBinder.GetBinder(current);
}
else
{
// If this ever breaks, make sure that all callers of
// CanHaveAssociatedLocalBinder are in sync.
Debug.Assert(!current.CanHaveAssociatedLocalBinder());
}
if (current == root)
{
break;
}
}
binder = binder ?? rootBinder.GetBinder(root) ?? rootBinder;
Debug.Assert(binder != null);
if (typeOfArgument != null && !typeOfEncounteredBeforeUnexpectedAnonymousFunction)
{
binder = new TypeofBinder(typeOfArgument, binder);
}
if (unexpectedAnonymousFunction != null)
{
binder = new ExecutableCodeBinder(unexpectedAnonymousFunction,
new LambdaSymbol(binder.ContainingMemberOrLambda,
unexpectedAnonymousFunction.Kind() == SyntaxKind.AnonymousMethodExpression ? MessageID.IDS_AnonMethod : MessageID.IDS_Lambda,
unexpectedAnonymousFunction,
isSynthesized: false),
binder);
}
if (typeOfArgument != null && typeOfEncounteredBeforeUnexpectedAnonymousFunction)
{
binder = new TypeofBinder(typeOfArgument, binder);
}
return binder;
}
private static Binder AdjustBinderForPositionWithinStatement(int position, Binder binder, StatementSyntax stmt)
{
switch (stmt.Kind())
{
case SyntaxKind.SwitchStatement:
var switchStmt = (SwitchStatementSyntax)stmt;
if (LookupPosition.IsBetweenTokens(position, switchStmt.OpenParenToken, switchStmt.OpenBraceToken))
{
binder = binder.GetBinder(switchStmt.Expression);
Debug.Assert(binder != null);
}
break;
case SyntaxKind.ForStatement:
var forStmt = (ForStatementSyntax)stmt;
if (LookupPosition.IsBetweenTokens(position, forStmt.SecondSemicolonToken, forStmt.CloseParenToken) &&
forStmt.Incrementors.Count > 0)
{
binder = binder.GetBinder(forStmt.Incrementors.First());
Debug.Assert(binder != null);
}
else if (LookupPosition.IsBetweenTokens(position, forStmt.FirstSemicolonToken, LookupPosition.GetFirstExcludedToken(forStmt)) &&
forStmt.Condition != null)
{
binder = binder.GetBinder(forStmt.Condition);
Debug.Assert(binder != null);
}
break;
case SyntaxKind.ForEachStatement:
case SyntaxKind.ForEachVariableStatement:
var foreachStmt = (CommonForEachStatementSyntax)stmt;
var start = stmt.Kind() == SyntaxKind.ForEachVariableStatement ? foreachStmt.InKeyword : foreachStmt.OpenParenToken;
if (LookupPosition.IsBetweenTokens(position, start, foreachStmt.Statement.GetFirstToken()))
{
binder = binder.GetBinder(foreachStmt.Expression);
Debug.Assert(binder != null);
}
break;
}
return binder;
}
public override Conversion ClassifyConversion(
ExpressionSyntax expression,
ITypeSymbol destination,
bool isExplicitInSource = false)
{
if ((object)destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
var csdestination = destination.EnsureCSharpSymbolOrNull("destination");
if (expression.Kind() == SyntaxKind.DeclarationExpression)
{
// Conversion from a declaration is unspecified.
return Conversion.NoConversion;
}
// Special Case: We have to treat anonymous functions differently, because of the way
// they are cached in the syntax-to-bound node map. Specifically, UnboundLambda nodes
// never appear in the map - they are converted to BoundLambdas, even in error scenarios.
// Since a BoundLambda has a type, we would end up doing a conversion from the delegate
// type, rather than from the anonymous function expression. If we use the overload that
// takes a position, it considers the request speculative and does not use the map.
// Bonus: Since the other overload will always bind the anonymous function from scratch,
// we don't have to worry about it affecting the trial-binding cache in the "real"
// UnboundLambda node (DevDiv #854548).
if (expression.IsAnonymousFunction())
{
CheckSyntaxNode(expression);
return this.ClassifyConversion(expression.SpanStart, expression, destination, isExplicitInSource);
}
if (isExplicitInSource)
{
return ClassifyConversionForCast(expression, csdestination);
}
// Note that it is possible for an expression to be convertible to a type
// via both an implicit user-defined conversion and an explicit built-in conversion.
// In that case, this method chooses the implicit conversion.
CheckSyntaxNode(expression);
var binder = this.GetEnclosingBinder(expression, GetAdjustedNodePosition(expression));
CSharpSyntaxNode bindableNode = this.GetBindableSyntaxNode(expression);
var boundExpression = this.GetLowerBoundNode(bindableNode) as BoundExpression;
if (binder == null || boundExpression == null)
{
return Conversion.NoConversion;
}
HashSet useSiteDiagnostics = null;
return binder.Conversions.ClassifyConversionFromExpression(boundExpression, csdestination, ref useSiteDiagnostics);
}
internal override Conversion ClassifyConversionForCast(
ExpressionSyntax expression,
TypeSymbol destination)
{
CheckSyntaxNode(expression);
if ((object)destination == null)
{
throw new ArgumentNullException(nameof(destination));
}
var binder = this.GetEnclosingBinder(expression, GetAdjustedNodePosition(expression));
CSharpSyntaxNode bindableNode = this.GetBindableSyntaxNode(expression);
var boundExpression = this.GetLowerBoundNode(bindableNode) as BoundExpression;
if (binder == null || boundExpression == null)
{
return Conversion.NoConversion;
}
HashSet useSiteDiagnostics = null;
return binder.Conversions.ClassifyConversionFromExpression(boundExpression, destination, ref useSiteDiagnostics, forCast: true);
}
///
/// Get the bound node corresponding to the root.
///
internal virtual BoundNode GetBoundRoot()
{
return GetUpperBoundNode(GetBindableSyntaxNode(this.Root));
}
///
/// Get the highest bound node in the tree associated with a particular syntax node.
///
internal BoundNode GetUpperBoundNode(CSharpSyntaxNode node, bool promoteToBindable = false)
{
if (promoteToBindable)
{
node = GetBindableSyntaxNode(node);
}
else
{
Debug.Assert(node == GetBindableSyntaxNode(node));
}
// The bound nodes are stored in the map from highest to lowest, so the first bound node is the highest.
var boundNodes = GetBoundNodes(node);
if (boundNodes.Length == 0)
{
return null;
}
else
{
return boundNodes[0];
}
}
///
/// Get the lowest bound node in the tree associated with a particular syntax node. Lowest is defined as last
/// in a pre-order traversal of the bound tree.
///
internal BoundNode GetLowerBoundNode(CSharpSyntaxNode node)
{
Debug.Assert(node == GetBindableSyntaxNode(node));
// The bound nodes are stored in the map from highest to lowest, so the last bound node is the lowest.
var boundNodes = GetBoundNodes(node);
if (boundNodes.Length == 0)
{
return null;
}
else
{
return boundNodes[boundNodes.Length - 1];
}
}
public override ImmutableArray GetSyntaxDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotSupportedException();
}
public override ImmutableArray GetDeclarationDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotSupportedException();
}
public override ImmutableArray GetMethodBodyDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotSupportedException();
}
public override ImmutableArray GetDiagnostics(TextSpan? span = null, CancellationToken cancellationToken = default(CancellationToken))
{
throw new NotSupportedException();
}
public override INamespaceSymbol GetDeclaredSymbol(NamespaceDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't defined namespace inside a member.
return null;
}
public override INamedTypeSymbol GetDeclaredSymbol(BaseTypeDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define type inside a member.
return null;
}
public override INamedTypeSymbol GetDeclaredSymbol(DelegateDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define type inside a member.
return null;
}
public override IFieldSymbol GetDeclaredSymbol(EnumMemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define member inside member.
return null;
}
public override ISymbol GetDeclaredSymbol(LocalFunctionStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
var boundLocalFunction = GetLowerBoundNode(declarationSyntax) as BoundLocalFunctionStatement;
return boundLocalFunction?.Symbol;
}
public override ISymbol GetDeclaredSymbol(MemberDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define member inside member.
return null;
}
public override IMethodSymbol GetDeclaredSymbol(BaseMethodDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define method inside member.
return null;
}
public override ISymbol GetDeclaredSymbol(BasePropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define property inside member.
return null;
}
public override IPropertySymbol GetDeclaredSymbol(PropertyDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define property inside member.
return null;
}
public override IPropertySymbol GetDeclaredSymbol(IndexerDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define indexer inside member.
return null;
}
public override IEventSymbol GetDeclaredSymbol(EventDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define event inside member.
return null;
}
public override IMethodSymbol GetDeclaredSymbol(AccessorDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define accessor inside member.
return null;
}
public override IMethodSymbol GetDeclaredSymbol(ArrowExpressionClauseSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define another member inside member.
return null;
}
public override ISymbol GetDeclaredSymbol(VariableDeclaratorSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declarationSyntax);
return GetDeclaredLocal(declarationSyntax, declarationSyntax.Identifier);
}
public override ISymbol GetDeclaredSymbol(SingleVariableDesignationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declarationSyntax);
return GetDeclaredLocal(declarationSyntax, declarationSyntax.Identifier);
}
private LocalSymbol GetDeclaredLocal(CSharpSyntaxNode declarationSyntax, SyntaxToken declaredIdentifier)
{
for (var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax)); binder != null; binder = binder.Next)
{
foreach (var local in binder.Locals)
{
if (local.IdentifierToken == declaredIdentifier)
{
return local;
}
}
}
return null;
}
public override ILabelSymbol GetDeclaredSymbol(LabeledStatementSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declarationSyntax);
var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax));
while (binder != null && !binder.IsLabelsScopeBinder)
{
binder = binder.Next;
}
if (binder != null)
{
foreach (var label in binder.Labels)
{
if (label.IdentifierNodeOrToken.IsToken &&
label.IdentifierNodeOrToken.AsToken() == declarationSyntax.Identifier)
{
return label;
}
}
}
return null;
}
public override ILabelSymbol GetDeclaredSymbol(SwitchLabelSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declarationSyntax);
var binder = this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax));
while (binder != null && !(binder is SwitchBinder))
{
binder = binder.Next;
}
if (binder != null)
{
foreach (var label in binder.Labels)
{
if (label.IdentifierNodeOrToken.IsNode &&
label.IdentifierNodeOrToken.AsNode() == declarationSyntax)
{
return label;
}
}
}
return null;
}
public override IAliasSymbol GetDeclaredSymbol(UsingDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define alias inside member.
return null;
}
public override IAliasSymbol GetDeclaredSymbol(ExternAliasDirectiveSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define an extern alias inside a member.
return null;
}
public override IParameterSymbol GetDeclaredSymbol(ParameterSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Could be parameter of lambda.
CheckSyntaxNode(declarationSyntax);
return GetLambdaParameterSymbol(declarationSyntax, cancellationToken);
}
internal override ImmutableArray GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define field inside member.
return ImmutableArray.Create();
}
private ParameterSymbol GetLambdaParameterSymbol(
ParameterSyntax parameter,
CancellationToken cancellationToken)
{
Debug.Assert(parameter != null);
var simpleLambda = parameter.Parent as SimpleLambdaExpressionSyntax;
if (simpleLambda != null)
{
return GetLambdaParameterSymbol(parameter, simpleLambda, cancellationToken);
}
var paramList = parameter.Parent as ParameterListSyntax;
if (paramList == null)
{
return null;
}
if (paramList.Parent == null || !paramList.Parent.IsAnonymousFunction())
{
return null;
}
return GetLambdaParameterSymbol(parameter, (ExpressionSyntax)paramList.Parent, cancellationToken);
}
private ParameterSymbol GetLambdaParameterSymbol(
ParameterSyntax parameter,
ExpressionSyntax lambda,
CancellationToken cancellationToken)
{
Debug.Assert(parameter != null);
Debug.Assert(lambda != null && lambda.IsAnonymousFunction());
// We should always be able to get at least an error binding for a lambda.
SymbolInfo symbolInfo = this.GetSymbolInfo(lambda, cancellationToken);
LambdaSymbol lambdaSymbol;
if ((object)symbolInfo.Symbol != null)
{
lambdaSymbol = (LambdaSymbol)symbolInfo.Symbol;
}
else if (symbolInfo.CandidateSymbols.Length == 1)
{
lambdaSymbol = (LambdaSymbol)symbolInfo.CandidateSymbols.Single();
}
else
{
Debug.Assert(this.GetMemberModel(lambda) == null, "Did not find a unique LambdaSymbol for lambda in member.");
return null;
}
return GetParameterSymbol(lambdaSymbol.Parameters, parameter, cancellationToken);
}
public override ITypeParameterSymbol GetDeclaredSymbol(TypeParameterSyntax typeParameter, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define alias inside member.
return null;
}
public override IRangeVariableSymbol GetDeclaredSymbol(JoinIntoClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
{
var bound = GetBoundQueryClause(node);
return bound == null ? null : bound.DefinedSymbol;
}
public override IRangeVariableSymbol GetDeclaredSymbol(QueryClauseSyntax queryClause, CancellationToken cancellationToken = default(CancellationToken))
{
var bound = GetBoundQueryClause(queryClause);
return bound == null ? null : bound.DefinedSymbol;
}
public override IRangeVariableSymbol GetDeclaredSymbol(QueryContinuationSyntax node, CancellationToken cancellationToken = default(CancellationToken))
{
var bound = GetBoundQueryClause(node);
return bound == null ? null : bound.DefinedSymbol;
}
public override AwaitExpressionInfo GetAwaitExpressionInfo(AwaitExpressionSyntax node)
{
if (node.Kind() != SyntaxKind.AwaitExpression)
{
throw new ArgumentException("node.Kind==" + node.Kind());
}
var bound = GetUpperBoundNode(node);
BoundAwaitExpression boundAwait = ((bound as BoundExpressionStatement)?.Expression ?? bound) as BoundAwaitExpression;
if (boundAwait == null)
{
return default(AwaitExpressionInfo);
}
return new AwaitExpressionInfo(boundAwait.GetAwaiter, boundAwait.IsCompleted, boundAwait.GetResult, boundAwait.IsDynamic);
}
public override ForEachStatementInfo GetForEachStatementInfo(ForEachStatementSyntax node)
{
return GetForEachStatementInfo((CommonForEachStatementSyntax)node);
}
public override ForEachStatementInfo GetForEachStatementInfo(CommonForEachStatementSyntax node)
{
BoundForEachStatement boundForEach = (BoundForEachStatement)GetUpperBoundNode(node);
if (boundForEach == null)
{
return default(ForEachStatementInfo);
}
ForEachEnumeratorInfo enumeratorInfoOpt = boundForEach.EnumeratorInfoOpt;
Debug.Assert(enumeratorInfoOpt != null || boundForEach.HasAnyErrors);
if (enumeratorInfoOpt == null)
{
return default(ForEachStatementInfo);
}
// Even though we usually pretend to be using System.Collection.IEnumerable
// for arrays, that doesn't make sense for pointer arrays since object
// (the type of System.Collections.IEnumerator.Current) isn't convertible
// to pointer types.
if (enumeratorInfoOpt.ElementType.IsPointerType())
{
Debug.Assert(!enumeratorInfoOpt.CurrentConversion.IsValid);
return default(ForEachStatementInfo);
}
// NOTE: we're going to list GetEnumerator, etc for array and string
// collections, even though we know that's not how the implementation
// actually enumerates them.
return new ForEachStatementInfo(
enumeratorInfoOpt.GetEnumeratorMethod,
enumeratorInfoOpt.MoveNextMethod,
(PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter.AssociatedSymbol,
enumeratorInfoOpt.NeedsDisposeMethod ? (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose) : null,
enumeratorInfoOpt.ElementType,
boundForEach.ElementConversion,
enumeratorInfoOpt.CurrentConversion);
}
private BoundQueryClause GetBoundQueryClause(CSharpSyntaxNode node)
{
CheckSyntaxNode(node);
return this.GetLowerBoundNode(node) as BoundQueryClause;
}
private QueryClauseInfo GetQueryClauseInfo(BoundQueryClause bound)
{
if (bound == null) return default(QueryClauseInfo);
var castInfo = (bound.Cast == null) ? default(SymbolInfo) : GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, bound.Cast, bound.Cast, boundNodeForSyntacticParent: null, binderOpt: null);
var operationInfo = GetSymbolInfoForQuery(bound);
return new QueryClauseInfo(castInfo: castInfo, operationInfo: operationInfo);
}
private SymbolInfo GetSymbolInfoForQuery(BoundQueryClause bound)
{
var call = bound?.Operation as BoundCall;
if (call == null)
{
return default(SymbolInfo);
}
var operation = call.IsDelegateCall ? call.ReceiverOpt : call;
return GetSymbolInfoForNode(SymbolInfoOptions.DefaultOptions, operation, operation, boundNodeForSyntacticParent: null, binderOpt: null);
}
private CSharpTypeInfo GetTypeInfoForQuery(BoundQueryClause bound)
{
return bound == null ?
CSharpTypeInfo.None :
GetTypeInfoForNode(bound, bound, bound);
}
public override QueryClauseInfo GetQueryClauseInfo(QueryClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken))
{
var bound = GetBoundQueryClause(node);
return GetQueryClauseInfo(bound);
}
public override IPropertySymbol GetDeclaredSymbol(AnonymousObjectMemberDeclaratorSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var anonymousObjectCreation = (AnonymousObjectCreationExpressionSyntax)declaratorSyntax.Parent;
if (anonymousObjectCreation == null)
{
return null;
}
var bound = this.GetLowerBoundNode(anonymousObjectCreation) as BoundAnonymousObjectCreationExpression;
if (bound == null)
{
return null;
}
var anonymousType = bound.Type as NamedTypeSymbol;
if ((object)anonymousType == null)
{
return null;
}
int index = anonymousObjectCreation.Initializers.IndexOf(declaratorSyntax);
Debug.Assert(index >= 0);
Debug.Assert(index < anonymousObjectCreation.Initializers.Count);
return AnonymousTypeManager.GetAnonymousTypeProperty(anonymousType, index);
}
public override INamedTypeSymbol GetDeclaredSymbol(AnonymousObjectCreationExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var bound = this.GetLowerBoundNode(declaratorSyntax) as BoundAnonymousObjectCreationExpression;
return (bound == null) ? null : bound.Type as NamedTypeSymbol;
}
public override INamedTypeSymbol GetDeclaredSymbol(TupleExpressionSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
return GetTypeOfTupleLiteral(declaratorSyntax);
}
public override ISymbol GetDeclaredSymbol(ArgumentSyntax declaratorSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
CheckSyntaxNode(declaratorSyntax);
var tupleLiteral = declaratorSyntax?.Parent as TupleExpressionSyntax;
// for now only arguments of a tuple literal may declare symbols
if (tupleLiteral == null)
{
return null;
}
var tupleLiteralType = GetTypeOfTupleLiteral(tupleLiteral);
if ((object)tupleLiteralType != null)
{
var elements = tupleLiteralType.TupleElements;
if(!elements.IsDefault)
{
var idx = tupleLiteral.Arguments.IndexOf(declaratorSyntax);
return elements[idx];
}
}
return null;
}
private NamedTypeSymbol GetTypeOfTupleLiteral(TupleExpressionSyntax declaratorSyntax)
{
var bound = this.GetLowerBoundNode(declaratorSyntax);
return (bound as BoundTupleExpression)?.Type as NamedTypeSymbol;
}
public override SyntaxTree SyntaxTree
{
get
{
return _root.SyntaxTree;
}
}
internal override IOperation GetOperationWorker(CSharpSyntaxNode node, GetOperationOptions options, CancellationToken cancellationToken)
{
CSharpSyntaxNode bindableNode;
BoundNode lowestBoundNode;
BoundNode highestBoundNode;
BoundNode boundParent;
GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
BoundNode result;
switch (options)
{
case GetOperationOptions.Parent:
result = boundParent;
break;
case GetOperationOptions.Highest:
result = highestBoundNode;
break;
case GetOperationOptions.Lowest:
default:
result = lowestBoundNode;
break;
}
return result as IOperation;
}
internal override SymbolInfo GetSymbolInfoWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
ValidateSymbolInfoOptions(options);
CSharpSyntaxNode bindableNode;
BoundNode lowestBoundNode;
BoundNode highestBoundNode;
BoundNode boundParent;
GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
return base.GetSymbolInfoForNode(options, lowestBoundNode, highestBoundNode, boundParent, binderOpt: null);
}
internal override CSharpTypeInfo GetTypeInfoWorker(CSharpSyntaxNode node, CancellationToken cancellationToken = default(CancellationToken))
{
CSharpSyntaxNode bindableNode;
BoundNode lowestBoundNode;
BoundNode highestBoundNode;
BoundNode boundParent;
GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
return GetTypeInfoForNode(lowestBoundNode, highestBoundNode, boundParent);
}
internal override ImmutableArray GetMemberGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
CSharpSyntaxNode bindableNode;
BoundNode lowestBoundNode;
BoundNode highestBoundNode;
BoundNode boundParent;
GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
return base.GetMemberGroupForNode(options, lowestBoundNode, boundParent, binderOpt: null);
}
internal override ImmutableArray GetIndexerGroupWorker(CSharpSyntaxNode node, SymbolInfoOptions options, CancellationToken cancellationToken = default(CancellationToken))
{
CSharpSyntaxNode bindableNode;
BoundNode lowestBoundNode;
BoundNode highestBoundNode;
BoundNode boundParent;
GetBoundNodes(node, out bindableNode, out lowestBoundNode, out highestBoundNode, out boundParent);
Debug.Assert(IsInTree(node), "Since the node is in the tree, we can always recompute the binder later");
return base.GetIndexerGroupForNode(lowestBoundNode, binderOpt: null);
}
internal override Optional