// 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.Concurrent;
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.Operations;
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 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>();
private Dictionary _lazyGuardedSynthesizedStatementsMap;
private ConcurrentDictionary _analyzedVariableTypesOpt;
private NullableWalker.SnapshotManager _lazySnapshotManager;
///
/// Only used when this is a speculative semantic model.
///
private readonly NullableWalker.SnapshotManager _parentSnapshotManagerOpt;
internal readonly Binder RootBinder;
///
/// Field specific to a non-speculative MemberSemanticModel that must have a containing semantic model.
///
private readonly SyntaxTreeSemanticModel _containingSemanticModelOpt;
// Fields specific to a speculative MemberSemanticModel.
private readonly SyntaxTreeSemanticModel _parentSemanticModelOpt;
private readonly int _speculatedPosition;
private readonly Lazy _operationFactory;
protected MemberSemanticModel(
CSharpSyntaxNode root,
Symbol memberSymbol,
Binder rootBinder,
SyntaxTreeSemanticModel containingSemanticModelOpt,
SyntaxTreeSemanticModel parentSemanticModelOpt,
NullableWalker.SnapshotManager snapshotManagerOpt,
int speculatedPosition)
{
Debug.Assert(root != null);
Debug.Assert((object)memberSymbol != null);
Debug.Assert(parentSemanticModelOpt == null ^ containingSemanticModelOpt == null);
Debug.Assert(containingSemanticModelOpt == null || !containingSemanticModelOpt.IsSpeculativeSemanticModel);
Debug.Assert(parentSemanticModelOpt == null || !parentSemanticModelOpt.IsSpeculativeSemanticModel, CSharpResources.ChainingSpeculativeModelIsNotSupported);
Debug.Assert(snapshotManagerOpt == null || parentSemanticModelOpt != null);
_root = root;
_memberSymbol = memberSymbol;
this.RootBinder = rootBinder.WithAdditionalFlags(GetSemanticModelBinderFlags());
_containingSemanticModelOpt = containingSemanticModelOpt;
_parentSemanticModelOpt = parentSemanticModelOpt;
_parentSnapshotManagerOpt = snapshotManagerOpt;
_speculatedPosition = speculatedPosition;
_operationFactory = new Lazy(() => new CSharpOperationFactory(this));
if (Compilation.NullableSemanticAnalysisEnabled)
{
_analyzedVariableTypesOpt = new ConcurrentDictionary();
}
}
public override CSharpCompilation Compilation
{
get
{
return (_containingSemanticModelOpt ?? _parentSemanticModelOpt).Compilation;
}
}
internal override CSharpSyntaxNode Root
{
get
{
return _root;
}
}
///
/// The member symbol
///
internal Symbol MemberSymbol
{
get
{
return _memberSymbol;
}
}
public sealed override bool IsSpeculativeSemanticModel
{
get
{
return _parentSemanticModelOpt != null;
}
}
public sealed override int OriginalPositionForSpeculation
{
get
{
return _speculatedPosition;
}
}
public sealed override CSharpSemanticModel ParentModel
{
get
{
return _parentSemanticModelOpt;
}
}
internal sealed override SemanticModel ContainingModelOrSelf
{
get
{
return _containingSemanticModelOpt ?? (SemanticModel)this;
}
}
internal override MemberSemanticModel GetMemberModel(SyntaxNode node)
{
// We do have to override this method, but should never call it because it might not do the right thing.
Debug.Assert(false);
return IsInTree(node) ? this : null;
}
///
/// This will cause the bound node cache to be populated if nullable semantic analysis is enabled.
///
protected virtual NullableWalker.SnapshotManager GetSnapshotManager()
{
EnsureNullabilityAnalysisPerformedIfNecessary();
Debug.Assert(_lazySnapshotManager is object || !Compilation.NullableSemanticAnalysisEnabled);
return _lazySnapshotManager;
}
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, GetSnapshotManager(), 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;
}
internal override BoundExpression GetSpeculativelyBoundExpression(int position, ExpressionSyntax expression, SpeculativeBindingOption bindingOption, out Binder binder, out ImmutableArray crefSymbols)
{
if (expression == null)
{
throw new ArgumentNullException(nameof(expression));
}
if (!Compilation.NullableSemanticAnalysisEnabled || bindingOption != SpeculativeBindingOption.BindAsExpression)
{
return GetSpeculativelyBoundExpressionWithoutNullability(position, expression, bindingOption, out binder, out crefSymbols);
}
crefSymbols = default;
position = CheckAndAdjustPosition(position);
expression = SyntaxFactory.GetStandaloneExpression(expression);
var bindableNewExpression = GetBindableSyntaxNode(expression);
binder = GetEnclosingBinder(position);
var boundRoot = Bind(binder, bindableNewExpression, _ignoredDiagnostics);
return (BoundExpression)NullableWalker.AnalyzeAndRewriteSpeculation(position, boundRoot, binder, GetSnapshotManager(), takeNewSnapshots: false, newSnapshots: out _);
}
private Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int position)
{
AssertPositionAdjusted(position);
return GetEnclosingBinderInternalWithinRoot(node, position, RootBinder, _root).WithAdditionalFlags(GetSemanticModelBinderFlags());
}
private static Binder GetEnclosingBinderInternalWithinRoot(SyntaxNode node, int position, Binder rootBinder, SyntaxNode root)
{
if (node == root)
{
return rootBinder.GetBinder(node) ?? rootBinder;
}
Debug.Assert(root.Contains(node));
ExpressionSyntax typeOfArgument = null;
LocalFunctionStatementSyntax ownerOfTypeParametersInScope = null;
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.LocalFunctionStatement)
{
Debug.Assert(ownerOfTypeParametersInScope == null);
var localFunction = (LocalFunctionStatementSyntax)stmt;
if (localFunction.TypeParameterList != null &&
!LookupPosition.IsBetweenTokens(position, localFunction.Identifier, localFunction.TypeParameterList.LessThanToken)) // Scope does not include method name.
{
ownerOfTypeParametersInScope = localFunction;
}
}
}
}
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.AnonymousFunctionBody());
Debug.Assert(binder != null);
}
}
else if (kind == SyntaxKind.TypeOfExpression &&
typeOfArgument == null &&
LookupPosition.IsBetweenTokens(
position,
(typeOfExpression = (TypeOfExpressionSyntax)current).OpenParenToken,
typeOfExpression.CloseParenToken))
{
typeOfArgument = typeOfExpression.Type;
}
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 (kind == SyntaxKind.ThisConstructorInitializer || kind == SyntaxKind.BaseConstructorInitializer)
{
binder = rootBinder.GetBinder(current);
}
else if (kind == SyntaxKind.ConstructorDeclaration)
{
binder = rootBinder.GetBinder(current);
}
else if (kind == SyntaxKind.SwitchExpression)
{
binder = rootBinder.GetBinder(current);
}
else if (kind == SyntaxKind.SwitchExpressionArm)
{
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 (ownerOfTypeParametersInScope != null)
{
LocalFunctionSymbol function = GetDeclaredLocalFunction(binder, ownerOfTypeParametersInScope.Identifier);
if ((object)function != null)
{
binder = function.SignatureBinder;
}
}
if (typeOfArgument != null)
{
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.SwitchKeyword, 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(nameof(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.GetEnclosingBinderInternal(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.GetEnclosingBinderInternal(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 GetLowerBoundNode(boundNodes);
}
}
private static BoundNode GetLowerBoundNode(ImmutableArray boundNodes)
{
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))
{
CheckSyntaxNode(declarationSyntax);
return GetDeclaredLocalFunction(declarationSyntax, declarationSyntax.Identifier);
}
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)
{
Debug.Assert(local is SourceLocalSymbol);
LocalSymbol adjustedLocal;
if (Compilation.NullableSemanticAnalysisEnabled)
{
if (!_analyzedVariableTypesOpt.TryGetValue(local, out adjustedLocal))
{
var types = GetSnapshotManager().GetVariableTypesForPosition(declarationSyntax.SpanStart);
// If the local was not inferred, it does not get an entry in this dictionary. Save the local mapped
// to itself to avoid needing to enter this code path in the future.
if (types.TryGetValue(local, out TypeWithAnnotations type))
{
adjustedLocal = _analyzedVariableTypesOpt.GetOrAdd(local, ((SourceLocalSymbol)local).WithAnalyzedType(type));
}
else
{
_analyzedVariableTypesOpt.TryAdd(local, local);
adjustedLocal = local;
}
}
}
else
{
adjustedLocal = local;
}
return adjustedLocal;
}
}
}
return null;
}
private LocalFunctionSymbol GetDeclaredLocalFunction(LocalFunctionStatementSyntax declarationSyntax, SyntaxToken declaredIdentifier)
{
return GetDeclaredLocalFunction(this.GetEnclosingBinder(GetAdjustedNodePosition(declarationSyntax)), declaredIdentifier);
}
private static LocalFunctionSymbol GetDeclaredLocalFunction(Binder enclosingBinder, SyntaxToken declaredIdentifier)
{
for (var binder = enclosingBinder; binder != null; binder = binder.Next)
{
foreach (var localFunction in binder.LocalFunctions)
{
if (localFunction.NameToken == declaredIdentifier)
{
return localFunction;
}
}
}
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 a lambda or a local function.
CheckSyntaxNode(declarationSyntax);
return GetLambdaOrLocalFunctionParameterSymbol(declarationSyntax, cancellationToken);
}
internal override ImmutableArray GetDeclaredSymbols(BaseFieldDeclarationSyntax declarationSyntax, CancellationToken cancellationToken = default(CancellationToken))
{
// Can't define field inside member.
return ImmutableArray.Create();
}
private ParameterSymbol GetLambdaOrLocalFunctionParameterSymbol(
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 || paramList.Parent == null)
{
return null;
}
if (paramList.Parent.IsAnonymousFunction())
{
return GetLambdaParameterSymbol(parameter, (ExpressionSyntax)paramList.Parent, cancellationToken);
}
else if (paramList.Parent.Kind() == SyntaxKind.LocalFunctionStatement)
{
var localFunction = (MethodSymbol)GetDeclaredSymbol((LocalFunctionStatementSyntax)paramList.Parent, cancellationToken);
if ((object)localFunction != null)
{
return GetParameterSymbol(localFunction.Parameters, parameter, cancellationToken);
}
}
return null;
}
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.AwaitableInfo);
}
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.
MethodSymbol disposeMethod = null;
if (enumeratorInfoOpt.NeedsDisposal)
{
if (!(enumeratorInfoOpt.DisposeMethod is null))
{
disposeMethod = enumeratorInfoOpt.DisposeMethod;
}
else
{
disposeMethod = enumeratorInfoOpt.IsAsync
? (MethodSymbol)Compilation.GetWellKnownTypeMember(WellKnownMember.System_IAsyncDisposable__DisposeAsync)
: (MethodSymbol)Compilation.GetSpecialTypeMember(SpecialMember.System_IDisposable__Dispose);
}
}
return new ForEachStatementInfo(
enumeratorInfoOpt.GetEnumeratorMethod,
enumeratorInfoOpt.MoveNextMethod,
currentProperty: (PropertySymbol)enumeratorInfoOpt.CurrentPropertyGetter?.AssociatedSymbol,
disposeMethod,
enumeratorInfoOpt.ElementType,
boundForEach.ElementConversion,
enumeratorInfoOpt.CurrentConversion);
}
public override DeconstructionInfo GetDeconstructionInfo(AssignmentExpressionSyntax node)
{
var boundDeconstruction = GetUpperBoundNode(node) as BoundDeconstructionAssignmentOperator;
if (boundDeconstruction is null)
{
return default;
}
var boundConversion = boundDeconstruction.Right;
Debug.Assert(boundConversion != null);
if (boundConversion is null)
{
return default;
}
return new DeconstructionInfo(boundConversion.Conversion);
}
public override DeconstructionInfo GetDeconstructionInfo(ForEachVariableStatementSyntax node)
{
var boundForEach = (BoundForEachStatement)GetUpperBoundNode(node);
if (boundForEach is null)
{
return default;
}
var boundDeconstruction = boundForEach.DeconstructionOpt;
Debug.Assert(boundDeconstruction != null || boundForEach.HasAnyErrors);
if (boundDeconstruction is null)
{
return default;
}
return new DeconstructionInfo(boundDeconstruction.DeconstructionAssignment.Right.Conversion);
}
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) ? SymbolInfo.None : 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 SymbolInfo.None;
}
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, CancellationToken cancellationToken)
{
CSharpSyntaxNode bindingRoot = GetBindingRootOrInitializer(node);
IOperation statementOrRootOperation = GetStatementOrRootOperation(bindingRoot, cancellationToken);
if (statementOrRootOperation == null)
{
return null;
}
// we might optimize it later
// https://github.com/dotnet/roslyn/issues/22180
return statementOrRootOperation.DescendantsAndSelf().FirstOrDefault(o => !o.IsImplicit && o.Syntax == node);
}
private CSharpSyntaxNode GetBindingRootOrInitializer(CSharpSyntaxNode node)
{
CSharpSyntaxNode bindingRoot = GetBindingRoot(node);
// if binding root is parameter, make it equal value
// we need to do this since node map doesn't contain bound node for parameter
if (bindingRoot is ParameterSyntax parameter && parameter.Default?.FullSpan.Contains(node.Span) == true)
{
return parameter.Default;
}
// if binding root is field variable declarator, make it initializer
// we need to do this since node map doesn't contain bound node for field/event variable declarator
if (bindingRoot is VariableDeclaratorSyntax variableDeclarator && variableDeclarator.Initializer?.FullSpan.Contains(node.Span) == true)
{
if (variableDeclarator.Parent?.Parent.IsKind(SyntaxKind.FieldDeclaration) == true ||
variableDeclarator.Parent?.Parent.IsKind(SyntaxKind.EventFieldDeclaration) == true)
{
return variableDeclarator.Initializer;
}
}
// if binding root is enum member declaration, make it equal value
// we need to do this since node map doesn't contain bound node for enum member decl
if (bindingRoot is EnumMemberDeclarationSyntax enumMember && enumMember.EqualsValue?.FullSpan.Contains(node.Span) == true)
{
return enumMember.EqualsValue;
}
// if binding root is property member declaration, make it equal value
// we need to do this since node map doesn't contain bound node for property initializer
if (bindingRoot is PropertyDeclarationSyntax propertyMember && propertyMember.Initializer?.FullSpan.Contains(node.Span) == true)
{
return propertyMember.Initializer;
}
return bindingRoot;
}
private IOperation GetStatementOrRootOperation(CSharpSyntaxNode node, CancellationToken cancellationToken)
{
Debug.Assert(node == GetBindingRootOrInitializer(node));
BoundNode highestBoundNode;
GetBoundNodes(node, out _, out _, out highestBoundNode, out _);
// decide whether we should use highest or lowest bound node here
// https://github.com/dotnet/roslyn/issues/22179
BoundNode result = highestBoundNode;
// The CSharp operation factory assumes that UnboundLambda will be bound for error recovery and never be passed to the factory
// as the start of a tree to get operations for. This is guaranteed by the builder that populates the node map, as it will call
// UnboundLambda.BindForErrorRecovery() when it encounters an UnboundLambda node.
Debug.Assert(result?.Kind != BoundKind.UnboundLambda);
return _operationFactory.Value.Create(result);
}
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