// We want the binder in which this syntax node is going to be bound, NOT the binder which // this syntax node *produces*. That is, suppose we have // // void M() { int x; { int y; { int z; } } } // // We want the enclosing binder of the syntax node for { int z; }. We do not want the binder // that has local z, but rather the binder that has local y. The inner block is going to be // bound in the context of its enclosing binder; it's contents are going to be bound in the // context of its binder. internal override Binder GetEnclosingBinderInternal(int position) { AssertPositionAdjusted(position); // If we have a root binder with no tokens in it, position can be outside the span event // after position is adjusted. If this happens, there can't be any if (!this.Root.FullSpan.Contains(position)) return this.RootBinder; SyntaxToken token = this.Root.FindToken(position); CSharpSyntaxNode node = (CSharpSyntaxNode)token.Parent; CSharpSyntaxNode innerLambda = GetInnermostLambdaOrQuery(node, position, allowStarting: true); // There are three possible scenarios here. // // 1) the node is outside all lambdas in this context, or // 2) The node is an outermost lambda in this context, or // 3) the node is inside the outermost lambda in this context. // // In the first case, no lambdas are involved at all so let's just fall back on the // original enclosing binder code. // // In the second case, we have been asked to bind an entire lambda and we know it to be // the outermost lambda in this context. Therefore the enclosing binder is going to be // the enclosing binder of this expression. However, we do not simply want to say // "here's the enclosing binder": // // void M() { Func<int, int> f = x=>x+1; } // // We should step out to the enclosing statement or expression, if there is one, and // bind that. if (innerLambda == null) { return GetEnclosingBinder(node, position); } // In the third case, we're in a child lambda. Have we already cached a binder for it? // If not, bind the outermost expression containing the lambda and then fill in the map. ImmutableArray<BoundNode> nodes; using (_nodeMapLock.DisposableRead()) { nodes = GuardedGetBoundNodesFromMap(innerLambda); } if (nodes.IsDefaultOrEmpty) { CSharpSyntaxNode outerLambda = GetOutermostLambdaOrQuery(innerLambda); Debug.Assert(outerLambda != null); Debug.Assert(outerLambda != this.Root); CSharpSyntaxNode nodeToBind = GetBindingRoot(outerLambda); var statementBinder = GetEnclosingBinder(nodeToBind, position); Binder incrementalBinder = new IncrementalBinder(this, statementBinder); using (_nodeMapLock.DisposableWrite()) { BoundNode boundOuterExpression = this.Bind(incrementalBinder, nodeToBind, _ignoredDiagnostics); GuardedAddBoundTreeAndGetBoundNodeFromMap(innerLambda, boundOuterExpression); } } // If there is a bug in the binder such that we "lose" a sub-expression containing a // lambda, and never put bound state for it into the bound tree, then the bound lambda // that comes back from the map lookup will be null. This can occur in error recovery // situations. If it is null, we fall back to the outer binder. using (_nodeMapLock.DisposableRead()) { nodes = GuardedGetBoundNodesFromMap(innerLambda); } if (nodes.IsDefaultOrEmpty) { return GetEnclosingBinder(node, position); } BoundNode boundInnerLambda = GetLowerBoundNode(innerLambda); Debug.Assert(boundInnerLambda != null); Binder result; switch (boundInnerLambda.Kind) { case BoundKind.UnboundLambda: boundInnerLambda = ((UnboundLambda)boundInnerLambda).BindForErrorRecovery(); goto case BoundKind.Lambda; case BoundKind.Lambda: AssertPositionAdjusted(position); result = GetLambdaEnclosingBinder(position, node, innerLambda, ((BoundLambda)boundInnerLambda).Binder); break; case BoundKind.QueryClause: result = GetQueryEnclosingBinder(position, node, ((BoundQueryClause)boundInnerLambda)); break; default: return GetEnclosingBinder(node, position); // Known to return non-null with BinderFlags.SemanticModel. } Debug.Assert(result != null); return result.WithAdditionalFlags(GetSemanticModelBinderFlags()); }
/// <summary> /// Get all bounds nodes associated with a node, ordered from highest to lowest in the bound tree. /// Strictly speaking, the order is that of a pre-order traversal of the bound tree. /// </summary> internal ImmutableArray<BoundNode> GetBoundNodes(CSharpSyntaxNode node) { // If this method is called with a null parameter, that implies that the Root should be // bound, but make sure that the Root is bindable. if (node == null) { node = GetBindableSyntaxNode(Root); } Debug.Assert(node == GetBindableSyntaxNode(node)); // We have one SemanticModel for each method. // // The SemanticModel contains a lazily-built immutable map from scope-introducing // syntactic statements (such as blocks) to binders, but not from lambdas to binders. // // The SemanticModel also contains a mutable map from syntax to bound nodes; that is // declared here. Since the map is not thread-safe we ensure that it is guarded with a // reader-writer lock. // // Have we already got the desired bound node in the mutable map? If so, return it. ImmutableArray<BoundNode> results; using (_nodeMapLock.DisposableRead()) { results = GuardedGetBoundNodesFromMap(node); } if (!results.IsDefaultOrEmpty) { return results; } // We might not actually have been given an expression or statement even though we were // allegedly given something that is "bindable". // If we didn't find in the cached bound nodes, find a binding root and bind it. // This will cache bound nodes under the binding root. CSharpSyntaxNode nodeToBind = GetBindingRoot(node); var statementBinder = GetEnclosingBinder(GetAdjustedNodePosition(nodeToBind)); Binder incrementalBinder = new IncrementalBinder(this, statementBinder); using (_nodeMapLock.DisposableWrite()) { BoundNode boundStatement = this.Bind(incrementalBinder, nodeToBind, _ignoredDiagnostics); results = GuardedAddBoundTreeAndGetBoundNodeFromMap(node, boundStatement); } if (!results.IsDefaultOrEmpty) { return results; } // If we still didn't find it, its still possible we could bind it directly. // For example, types are usually not represented by bound nodes, and some error conditions and // not yet implemented features do not create bound nodes for everything underneath them. // // In this case, however, we only add the single bound node we found to the map, not any child bound nodes, // to avoid duplicates in the map if a parent of this node comes through this code path also. var binder = GetEnclosingBinder(GetAdjustedNodePosition(node)); using (_nodeMapLock.DisposableRead()) { results = GuardedGetBoundNodesFromMap(node); } if (results.IsDefaultOrEmpty) { using (_nodeMapLock.DisposableWrite()) { var boundNode = this.Bind(binder, node, _ignoredDiagnostics); GuardedAddBoundTreeForStandaloneSyntax(node, boundNode); results = GuardedGetBoundNodesFromMap(node); } if (!results.IsDefaultOrEmpty) { return results; } } else { return results; } return ImmutableArray<BoundNode>.Empty; }
internal override Binder WithPrimaryConstructorParametersIfNecessary(NamedTypeSymbol containingType) { Binder result = base.WithPrimaryConstructorParametersIfNecessary(containingType); if (result != this) { result = new IncrementalBinder(this.semanticModel, result); } return result; }